mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-13 23:41:16 +00:00
refactor: markup render (#38864)
1. add missing CSP header to api & web render endpoints. 2. make jupyter render skip post-processors, nothing to process 3. make ShortLinkProcessor correctly validate URL schemes and respect the CustomURLSchemes setting
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"mvdan.cc/xurls/v2"
|
||||
)
|
||||
|
||||
type globalVarsType struct {
|
||||
schemeRegexp *regexp.Regexp // extract the scheme part from a link
|
||||
|
||||
wwwURLRegexp *regexp.Regexp // matching "www.{any-site}/{any-path}" pattern
|
||||
LinkifyRegex *regexp.Regexp // fast matching a URL link (powered by "xurls" package with custom schemes), no any extra validation.
|
||||
|
||||
allowedSchemes []string // nil means "allow all" (but disable the unsafe ones)
|
||||
DisallowedSchemes []string
|
||||
}
|
||||
|
||||
const regexpScheme = `[a-zA-Z][-+.a-zA-Z0-9]*`
|
||||
|
||||
var GlobalVars = sync.OnceValue(func() *globalVarsType {
|
||||
v := &globalVarsType{}
|
||||
v.schemeRegexp = regexp.MustCompile(`^` + regexpScheme + `:`)
|
||||
v.wwwURLRegexp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}((?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
|
||||
v.LinkifyRegex, _ = xurls.StrictMatchingScheme("https?://")
|
||||
v.allowedSchemes = []string{"http", "https"}
|
||||
v.DisallowedSchemes = []string{"data", "javascript", "vbscript"}
|
||||
return v
|
||||
})
|
||||
|
||||
type CheckLinkURLSchemeResult struct {
|
||||
HasScheme, AllowToLinkify bool
|
||||
}
|
||||
|
||||
func CheckLinkURLScheme(link string) CheckLinkURLSchemeResult {
|
||||
vars := GlobalVars()
|
||||
m := vars.schemeRegexp.FindStringSubmatch(link)
|
||||
if m == nil {
|
||||
return CheckLinkURLSchemeResult{AllowToLinkify: true} // relative link is always valid
|
||||
}
|
||||
urlScheme := strings.ToLower(m[0])
|
||||
urlScheme = urlScheme[0 : len(urlScheme)-1] // remove the trailing ":"
|
||||
allowed := len(vars.allowedSchemes) == 0 || slices.Contains(vars.allowedSchemes, urlScheme)
|
||||
disabled := slices.Contains(vars.DisallowedSchemes, urlScheme)
|
||||
return CheckLinkURLSchemeResult{HasScheme: true, AllowToLinkify: allowed && !disabled}
|
||||
}
|
||||
|
||||
func InitLinkURLSchemes(customSchemes []string) {
|
||||
validScheme := regexp.MustCompile(`^` + regexpScheme + `$`)
|
||||
schemes := container.Set[string]{}
|
||||
for _, scheme := range customSchemes {
|
||||
schemeLower := strings.ToLower(scheme)
|
||||
if !validScheme.MatchString(schemeLower) {
|
||||
log.Error("Invalid custom URL scheme %q", scheme)
|
||||
continue
|
||||
}
|
||||
schemes.Add(schemeLower)
|
||||
}
|
||||
|
||||
// HINT: CUSTOM-URL-SCHEMES-ALLOW: setting custom means also allow them besides http/https, no custom means "allow all"
|
||||
if len(schemes) > 0 {
|
||||
schemes.AddMultiple("http", "https")
|
||||
linkifyRegexps := make([]string, 0, len(schemes))
|
||||
for _, s := range schemes.Values() {
|
||||
s += util.Iif(slices.Contains(xurls.SchemesNoAuthority, s), ":", "://")
|
||||
linkifyRegexps = append(linkifyRegexps, regexp.QuoteMeta(s))
|
||||
}
|
||||
GlobalVars().LinkifyRegex, _ = xurls.StrictMatchingScheme(strings.Join(linkifyRegexps, "|"))
|
||||
GlobalVars().allowedSchemes = schemes.Values()
|
||||
} else {
|
||||
GlobalVars().LinkifyRegex, _ = xurls.StrictMatchingScheme("https?://") // only auto-linkify http and https
|
||||
GlobalVars().allowedSchemes = nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user