mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-12 14:08:22 +00:00
backport #38864 1. add missing CSP header to api & web render endpoints. 2. make jupyter render skip post-processors, nothing to process
This commit is contained in:
@@ -188,6 +188,26 @@ func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
|
||||
return b.Locale.TrN(cnt, key1, keyN, args...)
|
||||
}
|
||||
|
||||
func CspScriptNonce(ctx reqctx.RequestContext) (ret string) {
|
||||
// Generate a random nonce for each request and cache it in the context to make it usable during the whole rendering process.
|
||||
//
|
||||
// Some "<script>" tags are not in the CSP context, so they don't need nonce,
|
||||
// these tags are written as "<script nonce>" to help developers to know that "no script nonce attribute is missing"
|
||||
// (e.g.: when they grep the codebase for "script" tags)
|
||||
ret, _ = ctx.Value("_cspScriptNonce").(string)
|
||||
if ret == "" {
|
||||
ret = util.FastCryptoRandomHex(32) // 16 bytes / 128 bits entropy
|
||||
ctx.SetContextValue("_cspScriptNonce", ret)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *Base) SetHeaderContentSecurityPolicyGeneral() {
|
||||
if csp := WebContentSecurityPolicy(CspScriptNonce(b)); csp != "" {
|
||||
b.Resp.Header().Set("Content-Security-Policy", csp)
|
||||
}
|
||||
}
|
||||
|
||||
func NewBaseContext(resp http.ResponseWriter, req *http.Request) *Base {
|
||||
reqCtx := reqctx.FromContext(req.Context())
|
||||
b := &Base{
|
||||
|
||||
@@ -12,10 +12,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/htmlutil"
|
||||
"gitea.dev/modules/httplib"
|
||||
"gitea.dev/modules/public"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/services/webtheme"
|
||||
)
|
||||
@@ -24,16 +25,16 @@ type TemplateContext map[string]any
|
||||
|
||||
var _ context.Context = TemplateContext(nil)
|
||||
|
||||
func NewTemplateContext(ctx context.Context, req *http.Request) TemplateContext {
|
||||
func NewTemplateContext(ctx reqctx.RequestContext, req *http.Request) TemplateContext {
|
||||
return TemplateContext{"_ctx": ctx, "_req": req}
|
||||
}
|
||||
|
||||
func (c TemplateContext) req() *http.Request {
|
||||
return c["_req"].(*http.Request)
|
||||
return c["_req"].(*http.Request) //nolint:forcetypeassert // must exist
|
||||
}
|
||||
|
||||
func (c TemplateContext) parentContext() context.Context {
|
||||
return c["_ctx"].(context.Context)
|
||||
func (c TemplateContext) parentContext() reqctx.RequestContext {
|
||||
return c["_ctx"].(reqctx.RequestContext) //nolint:forcetypeassert // must exist
|
||||
}
|
||||
|
||||
func (c TemplateContext) Deadline() (deadline time.Time, ok bool) {
|
||||
@@ -100,21 +101,10 @@ func (c TemplateContext) ScriptImport(path string, typ ...string) template.HTML
|
||||
}
|
||||
|
||||
func (c TemplateContext) CspScriptNonce() (ret string) {
|
||||
// Generate a random nonce for each request and cache it in the context to make it usable during the whole rendering process.
|
||||
//
|
||||
// Some "<script>" tags are not in the CSP context, so they don't need nonce,
|
||||
// these tags are written as "<script nonce>" to help developers to know that "no script nonce attribute is missing"
|
||||
// (e.g.: when they grep the codebase for "script" tags)
|
||||
|
||||
ret, _ = c["_cspScriptNonce"].(string)
|
||||
if ret == "" {
|
||||
ret = util.FastCryptoRandomHex(32) // 16 bytes / 128 bits entropy
|
||||
c["_cspScriptNonce"] = ret
|
||||
}
|
||||
return ret
|
||||
return CspScriptNonce(c.parentContext())
|
||||
}
|
||||
|
||||
func (c TemplateContext) HeadMetaContentSecurityPolicy() template.HTML {
|
||||
func WebContentSecurityPolicy(scriptNonce string) string {
|
||||
if setting.Security.ContentSecurityPolicyGeneral == "unset" {
|
||||
return "" // if site admin disables the general CSP, then we don't use it
|
||||
}
|
||||
@@ -130,16 +120,24 @@ func (c TemplateContext) HeadMetaContentSecurityPolicy() template.HTML {
|
||||
// * Browsers will merge and use the stricter rules between Gitea and reverse proxy
|
||||
// B. Introduce some config options in "app.ini"
|
||||
// * Maybe this approach should be avoided, don't make the config system too complex, just let users use A
|
||||
return template.HTML(`<meta http-equiv="Content-Security-Policy" content="` +
|
||||
// allow all by default (the same as old releases with no CSP)
|
||||
// * maybe some images or markup (external) renders need "data:", need to investigate
|
||||
// * avatar upload editor needs "blob:", at least "img-src" and "content-src"
|
||||
`default-src * data: blob:;` +
|
||||
|
||||
// allow all by default (the same as old releases with no CSP)
|
||||
// * maybe some images or markup (external) renders need "data:", need to investigate
|
||||
// * avatar upload editor needs "blob:", at least "img-src" and "content-src"
|
||||
return `default-src * data: blob:;` +
|
||||
|
||||
// enforce nonce for all scripts, disallow inline scripts
|
||||
`script-src * 'nonce-` + c.CspScriptNonce() + `';` +
|
||||
`script-src * 'nonce-` + scriptNonce + `';` +
|
||||
|
||||
// it seems that Vue needs the unsafe-inline, and our custom colors (e.g.: label) also need it
|
||||
`style-src * 'unsafe-inline';` +
|
||||
`">`)
|
||||
`style-src * 'unsafe-inline';`
|
||||
}
|
||||
|
||||
func (c TemplateContext) HeadMetaContentSecurityPolicy() template.HTML {
|
||||
scriptNonce := c.CspScriptNonce()
|
||||
csp := WebContentSecurityPolicy(scriptNonce)
|
||||
if csp == "" {
|
||||
return ""
|
||||
}
|
||||
return htmlutil.HTMLFormat(`<meta http-equiv="Content-Security-Policy" content="%s">`, csp)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
@@ -57,7 +58,7 @@ func TestAppFullLink(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLNever)()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://gitea.example.com/sub/", nil)
|
||||
tmplCtx := NewTemplateContext(req.Context(), req)
|
||||
tmplCtx := NewTemplateContext(reqctx.NewRequestContextForTest(req.Context()), req)
|
||||
|
||||
assert.Equal(t, "https://gitea.example.com/sub", string(tmplCtx.AppFullLink()))
|
||||
assert.Equal(t, "https://gitea.example.com/sub/user/repo", string(tmplCtx.AppFullLink("user/repo")))
|
||||
|
||||
Reference in New Issue
Block a user