Support inline rendering of CUSTOM_URL_SCHEMES (#8496)
* Support inline rendering of CUSTOM_URL_SCHEMES * Fix lint * Add tests * Fix lint
This commit is contained in:
parent
8ad2697611
commit
cea8ea5ae6
4 changed files with 68 additions and 13 deletions
|
@ -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,22 +28,28 @@ 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")
|
||||
|
||||
// Checkboxes
|
||||
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
|
||||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
|
||||
|
||||
// Custom URL-Schemes
|
||||
sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
|
||||
|
||||
// Allow keyword markup
|
||||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span")
|
||||
ReplaceSanitizer()
|
||||
})
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
// Checkboxes
|
||||
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
|
||||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
|
||||
|
||||
// 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.
|
||||
func Sanitize(s string) string {
|
||||
NewSanitizer()
|
||||
|
|
Reference in a new issue