* Support inline rendering of CUSTOM_URL_SCHEMES * Fix lint * Add tests * Fix linttags/v1.11.0-rc1
@@ -92,6 +92,32 @@ func getIssueFullPattern() *regexp.Regexp { | |||
return issueFullPattern | |||
} | |||
// CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text | |||
func CustomLinkURLSchemes(schemes []string) { | |||
schemes = append(schemes, "http", "https") | |||
withAuth := make([]string, 0, len(schemes)) | |||
validScheme := regexp.MustCompile(`^[a-z]+$`) | |||
for _, s := range schemes { | |||
if !validScheme.MatchString(s) { | |||
continue | |||
} | |||
without := false | |||
for _, sna := range xurls.SchemesNoAuthority { | |||
if s == sna { | |||
without = true | |||
break | |||
} | |||
} | |||
if without { | |||
s += ":" | |||
} else { | |||
s += "://" | |||
} | |||
withAuth = append(withAuth, s) | |||
} | |||
linkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|")) | |||
} | |||
// IsSameDomain checks if given url string has the same hostname as current Gitea instance | |||
func IsSameDomain(s string) bool { | |||
if strings.HasPrefix(s, "/") { |
@@ -89,6 +89,11 @@ func TestRender_links(t *testing.T) { | |||
} | |||
// Text that should be turned into URL | |||
defaultCustom := setting.Markdown.CustomURLSchemes | |||
setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"} | |||
ReplaceSanitizer() | |||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) | |||
test( | |||
"https://www.example.com", | |||
`<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`) | |||
@@ -131,6 +136,12 @@ func TestRender_links(t *testing.T) { | |||
test( | |||
"https://username:password@gitea.com", | |||
`<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`) | |||
test( | |||
"ftp://gitea.com/file.txt", | |||
`<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`) | |||
test( | |||
"magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download", | |||
`<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p>`) | |||
// Test that should *not* be turned into URL | |||
test( | |||
@@ -154,6 +165,14 @@ func TestRender_links(t *testing.T) { | |||
test( | |||
"www", | |||
`<p>www</p>`) | |||
test( | |||
"ftps://gitea.com", | |||
`<p>ftps://gitea.com</p>`) | |||
// Restore previous settings | |||
setting.Markdown.CustomURLSchemes = defaultCustom | |||
ReplaceSanitizer() | |||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) | |||
} | |||
func TestRender_email(t *testing.T) { |
@@ -9,12 +9,16 @@ import ( | |||
"strings" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
) | |||
// Init initialize regexps for markdown parsing | |||
func Init() { | |||
getIssueFullPattern() | |||
NewSanitizer() | |||
if len(setting.Markdown.CustomURLSchemes) > 0 { | |||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) | |||
} | |||
// since setting maybe changed extensions, this will reload all parser extensions mapping | |||
extParsers = make(map[string]Parser) |
@@ -28,20 +28,26 @@ var sanitizer = &Sanitizer{} | |||
// entire application lifecycle. | |||
func NewSanitizer() { | |||
sanitizer.init.Do(func() { | |||
sanitizer.policy = bluemonday.UGCPolicy() | |||
// We only want to allow HighlightJS specific classes for code blocks | |||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code") | |||
ReplaceSanitizer() | |||
}) | |||
} | |||
// Checkboxes | |||
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") | |||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input") | |||
// ReplaceSanitizer replaces the current sanitizer to account for changes in settings | |||
func ReplaceSanitizer() { | |||
sanitizer = &Sanitizer{} | |||
sanitizer.policy = bluemonday.UGCPolicy() | |||
// We only want to allow HighlightJS specific classes for code blocks | |||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code") | |||
// Custom URL-Schemes | |||
sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) | |||
// Checkboxes | |||
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") | |||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input") | |||
// Allow keyword markup | |||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span") | |||
}) | |||
// Custom URL-Schemes | |||
sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) | |||
// Allow keyword markup | |||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span") | |||
} | |||
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. |