8978817634
Signed-off-by: Odyssey <odyssey346@disroot.org>
311 lines
8.1 KiB
Go
311 lines
8.1 KiB
Go
package pages
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/carlmjohnson/requests"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gomarkdown/markdown"
|
|
"github.com/microcosm-cc/bluemonday"
|
|
|
|
"codeberg.org/Odyssium/gothub/utils"
|
|
|
|
"context"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type User struct {
|
|
Login string
|
|
Name string
|
|
Bio string
|
|
AvatarUrl string
|
|
Location string
|
|
Following int64
|
|
Followers int64
|
|
Link string
|
|
Company string
|
|
Type string
|
|
EwTwitter string
|
|
Readme string
|
|
}
|
|
|
|
type Ratelimit struct {
|
|
Remaining int64
|
|
Limit int64
|
|
}
|
|
|
|
// HandleUser handles the user page.
|
|
func HandleUser(c *fiber.Ctx) error {
|
|
user := utils.GetRequest("https://api.github.com/users/" + c.Params("user"))
|
|
if user.Get("message").String() == "Not Found" {
|
|
return c.Status(404).Render("error", fiber.Map{
|
|
"error": "User " + c.Params("user") + " not found",
|
|
})
|
|
}
|
|
if strings.Contains(user.Get("message").String(), "rate limit") { // dont wanna get the status code so i'll just do this instead 👍
|
|
ratelimitJSON := utils.GetRequest("https://api.github.com/rate_limit")
|
|
fmt.Println(ratelimitJSON)
|
|
var ratelimitArray []Ratelimit
|
|
|
|
ratelimitArray = append(ratelimitArray, Ratelimit{
|
|
Remaining: ratelimitJSON.Get("resources.core.remaining").Int(),
|
|
Limit: ratelimitJSON.Get("resources.core.limit").Int(),
|
|
})
|
|
|
|
fmt.Println(ratelimitArray)
|
|
|
|
return c.Render("ratelimit", fiber.Map{
|
|
"Title": "GitHub API /users endpoint rate limit exceeded",
|
|
"ratelimit": ratelimitArray,
|
|
})
|
|
} else {
|
|
var userArray []User
|
|
|
|
var readmee string
|
|
|
|
err := requests.
|
|
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("user") + "/master/README.md").
|
|
ToString(&readmee).
|
|
Fetch(context.Background())
|
|
if err != nil {
|
|
readmee = ""
|
|
log.Println(err)
|
|
}
|
|
|
|
mightBeUnsafe := markdown.ToHTML([]byte(readmee), nil, nil)
|
|
|
|
// Trust Nobody
|
|
readmeOutput := UGCPolicy().SanitizeBytes(mightBeUnsafe)
|
|
|
|
var link string
|
|
|
|
if user.Get("blog").String() == "" {
|
|
link = ""
|
|
} else {
|
|
link = user.Get("blog").String()
|
|
if strings.HasPrefix(link, "https://") {
|
|
link = strings.TrimPrefix(link, "https://")
|
|
} else if strings.HasPrefix(link, "http://") {
|
|
link = strings.TrimPrefix(link, "http://")
|
|
} else {
|
|
log.Println("Has no prefix")
|
|
}
|
|
}
|
|
|
|
userArray = append(userArray, User{
|
|
Login: user.Get("login").String(),
|
|
Name: user.Get("name").String(),
|
|
Bio: user.Get("bio").String(),
|
|
AvatarUrl: "/avatar/" + user.Get("id").String(),
|
|
Location: user.Get("location").String(),
|
|
Following: user.Get("following").Int(),
|
|
Followers: user.Get("followers").Int(),
|
|
Link: link,
|
|
Company: user.Get("company").String(),
|
|
Type: user.Get("type").String(),
|
|
EwTwitter: user.Get("twitter_username").String(),
|
|
Readme: string(readmeOutput),
|
|
})
|
|
|
|
fmt.Println(userArray)
|
|
|
|
return c.Render("user", fiber.Map{
|
|
"user": userArray,
|
|
})
|
|
}
|
|
}
|
|
|
|
// copied from bluemonday's GitHub repostiory, with some adaptations
|
|
func UGCPolicy() *bluemonday.Policy {
|
|
|
|
p := bluemonday.NewPolicy()
|
|
|
|
///////////////////////
|
|
// Global attributes //
|
|
///////////////////////
|
|
|
|
// "class" is not permitted as we are not allowing users to style their own
|
|
// content
|
|
|
|
p.AllowStandardAttributes()
|
|
|
|
//////////////////////////////
|
|
// Global URL format policy //
|
|
//////////////////////////////
|
|
|
|
p.AllowStandardURLs()
|
|
|
|
////////////////////////////////
|
|
// Declarations and structure //
|
|
////////////////////////////////
|
|
|
|
// "xml" "xslt" "DOCTYPE" "html" "head" are not permitted as we are
|
|
// expecting user generated content to be a fragment of HTML and not a full
|
|
// document.
|
|
|
|
//////////////////////////
|
|
// Sectioning root tags //
|
|
//////////////////////////
|
|
|
|
// "article" and "aside" are permitted and takes no attributes
|
|
p.AllowElements("article", "aside")
|
|
|
|
// "body" is not permitted as we are expecting user generated content to be a fragment
|
|
// of HTML and not a full document.
|
|
|
|
// "details" is permitted, including the "open" attribute which can either
|
|
// be blank or the value "open".
|
|
p.AllowAttrs(
|
|
"open",
|
|
).Matching(regexp.MustCompile(`(?i)^(|open)$`)).OnElements("details")
|
|
|
|
// "fieldset" is not permitted as we are not allowing forms to be created.
|
|
|
|
// "figure" is permitted and takes no attributes
|
|
p.AllowElements("figure")
|
|
|
|
// "nav" is not permitted as it is assumed that the site (and not the user)
|
|
// has defined navigation elements
|
|
|
|
// "section" is permitted and takes no attributes
|
|
p.AllowElements("section")
|
|
|
|
// "summary" is permitted and takes no attributes
|
|
p.AllowElements("summary")
|
|
|
|
//////////////////////////
|
|
// Headings and footers //
|
|
//////////////////////////
|
|
|
|
// "footer" is not permitted as we expect user content to be a fragment and
|
|
// not structural to this extent
|
|
|
|
// "h1" through "h6" are permitted and take no attributes
|
|
p.AllowElements("h1", "h2", "h3", "h4", "h5", "h6")
|
|
|
|
// "header" is not permitted as we expect user content to be a fragment and
|
|
// not structural to this extent
|
|
|
|
// "hgroup" is permitted and takes no attributes
|
|
p.AllowElements("hgroup")
|
|
|
|
/////////////////////////////////////
|
|
// Content grouping and separating //
|
|
/////////////////////////////////////
|
|
|
|
// "blockquote" is permitted, including the "cite" attribute which must be
|
|
// a standard URL.
|
|
p.AllowAttrs("cite").OnElements("blockquote")
|
|
|
|
// "br" "div" "hr" "p" "span" "wbr" are permitted and take no attributes
|
|
p.AllowElements("br", "div", "hr", "p", "span", "wbr")
|
|
|
|
///////////
|
|
// Links //
|
|
///////////
|
|
|
|
// "a" is permitted
|
|
p.AllowAttrs("href").OnElements("a")
|
|
|
|
// "area" is permitted along with the attributes that map image maps work
|
|
p.AllowAttrs("name").Matching(
|
|
regexp.MustCompile(`^([\p{L}\p{N}_-]+)$`),
|
|
).OnElements("map")
|
|
p.AllowAttrs("alt").Matching(bluemonday.Paragraph).OnElements("area")
|
|
p.AllowAttrs("coords").Matching(
|
|
regexp.MustCompile(`^([0-9]+,)+[0-9]+$`),
|
|
).OnElements("area")
|
|
p.AllowAttrs("href").OnElements("area")
|
|
p.AllowAttrs("rel").Matching(bluemonday.SpaceSeparatedTokens).OnElements("area")
|
|
p.AllowAttrs("shape").Matching(
|
|
regexp.MustCompile(`(?i)^(default|circle|rect|poly)$`),
|
|
).OnElements("area")
|
|
p.AllowAttrs("usemap").Matching(
|
|
regexp.MustCompile(`(?i)^#[\p{L}\p{N}_-]+$`),
|
|
).OnElements("img")
|
|
|
|
// "link" is not permitted
|
|
|
|
/////////////////////
|
|
// Phrase elements //
|
|
/////////////////////
|
|
|
|
// The following are all inline phrasing elements
|
|
p.AllowElements("abbr", "acronym", "cite", "code", "dfn", "em",
|
|
"figcaption", "mark", "s", "samp", "strong", "sub", "sup", "var")
|
|
|
|
// "q" is permitted and "cite" is a URL and handled by URL policies
|
|
p.AllowAttrs("cite").OnElements("q")
|
|
|
|
// "time" is permitted
|
|
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("time")
|
|
|
|
////////////////////
|
|
// Style elements //
|
|
////////////////////
|
|
|
|
// block and inline elements that impart no semantic meaning but style the
|
|
// document
|
|
p.AllowElements("b", "i", "pre", "small", "strike", "tt", "u")
|
|
|
|
// "style" is not permitted as we are not yet sanitising CSS and it is an
|
|
// XSS attack vector
|
|
|
|
//////////////////////
|
|
// HTML5 Formatting //
|
|
//////////////////////
|
|
|
|
// "bdi" "bdo" are permitted
|
|
p.AllowAttrs("dir").Matching(bluemonday.Direction).OnElements("bdi", "bdo")
|
|
|
|
// "rp" "rt" "ruby" are permitted
|
|
p.AllowElements("rp", "rt", "ruby")
|
|
|
|
///////////////////////////
|
|
// HTML5 Change tracking //
|
|
///////////////////////////
|
|
|
|
// "del" "ins" are permitted
|
|
p.AllowAttrs("cite").Matching(bluemonday.Paragraph).OnElements("del", "ins")
|
|
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("del", "ins")
|
|
|
|
///////////
|
|
// Lists //
|
|
///////////
|
|
|
|
p.AllowLists()
|
|
|
|
////////////
|
|
// Tables //
|
|
////////////
|
|
|
|
p.AllowTables()
|
|
|
|
///////////
|
|
// Forms //
|
|
///////////
|
|
|
|
// By and large, forms are not permitted. However there are some form
|
|
// elements that can be used to present data, and we do permit those
|
|
//
|
|
// "button" "fieldset" "input" "keygen" "label" "output" "select" "datalist"
|
|
// "textarea" "optgroup" "option" are all not permitted
|
|
|
|
// "meter" is permitted
|
|
p.AllowAttrs(
|
|
"value",
|
|
"min",
|
|
"max",
|
|
"low",
|
|
"high",
|
|
"optimum",
|
|
).Matching(bluemonday.Number).OnElements("meter")
|
|
|
|
// "progress" is permitted
|
|
p.AllowAttrs("value", "max").Matching(bluemonday.Number).OnElements("progress")
|
|
|
|
return p
|
|
}
|