Skip to content

Use Content-Security-Policy: script nonce#37232

Merged
wxiaoguang merged 7 commits intogo-gitea:mainfrom
wxiaoguang:enable-csp-nonce
Apr 15, 2026
Merged

Use Content-Security-Policy: script nonce#37232
wxiaoguang merged 7 commits intogo-gitea:mainfrom
wxiaoguang:enable-csp-nonce

Conversation

@wxiaoguang
Copy link
Copy Markdown
Contributor

@wxiaoguang wxiaoguang commented Apr 15, 2026

Fix #305

⚠️ BREAKING ⚠️

For custom theme users only: if you used <script> tags in custom templates, you need to add the nonce attribute to them:

<script nonce="{{ctx.CspScriptNonce}}">
...
</script>

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Apr 15, 2026
@wxiaoguang wxiaoguang force-pushed the enable-csp-nonce branch 2 times, most recently from ad8cd4e to 23956fa Compare April 15, 2026 10:15
@silverwind silverwind changed the title Use Content-Seucrity-Policy: script nonce Use Content-Security-Policy: script nonce Apr 15, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-request CSP script nonces and updates templates/JS to apply nonces consistently, aiming to improve compatibility with stricter Content-Security-Policy configurations (Fix #305).

Changes:

  • Introduces ctx.CspScriptNonce, ctx.ScriptImport, and ctx.HeadMetaContentSecurityPolicy in TemplateContext and updates templates to use them.
  • Adds nonce="{{ctx.CspScriptNonce}}" to various inline/external <script> tags and propagates the nonce when dynamically executing scripts in the PR merge box.
  • Updates external markup rendering outputs/tests to include a nonce attribute on injected helper scripts.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web_src/js/features/repo-issue-pull.ts Propagates CSP nonce to dynamically executed scripts in the merge box reload path.
tests/integration/markup_external_test.go Updates expected HTML to include nonce="not-needed" in injected script tags.
templates/user/dashboard/repolist.tmpl Adds CSP nonce to inline module script.
templates/user/auth/captcha.tmpl Adds CSP nonce to captcha provider scripts.
templates/swagger/openapi-viewer.tmpl Adds CSP meta and switches to ctx.ScriptImport for nonce-aware script tags.
templates/status/500.tmpl Adds CSP meta and nonce to inline script on the error page.
templates/shared/combomarkdowneditor.tmpl Adds nonce to inline script used for editor styling toggle.
templates/repo/issue/view_content/pull_merge_box.tmpl Adds nonce to inline module script in merge UI.
templates/repo/diff/box.tmpl Adds nonces to inline scripts controlling diff file tree UI.
templates/base/head_script.tmpl Adds nonce to the global inline config script and switches to ctx.ScriptImport.
templates/base/head.tmpl Injects CSP meta into the base page head.
templates/base/footer.tmpl Switches to ctx.ScriptImport for the main JS entry.
services/context/context_template.go Adds CSP nonce generation, CSP meta emission, and nonce-aware script import helper.
services/context/context.go Moves TemplateContext type definition out (now in context_template.go).
modules/util/util.go Adds “fast” random helpers and changes CryptoRandomBytes error handling behavior.
modules/templates/helper.go Removes legacy global ScriptImport template func implementation.
modules/markup/render.go Adds nonce="not-needed" to external-render helper script injection.
modules/markup/external/openapi.go Adds nonce="not-needed" to swagger script tag in generated HTML.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/context/context_template.go Outdated
Comment thread modules/util/util.go Outdated
Comment thread modules/util/util.go
Comment thread web_src/js/features/repo-issue-pull.ts Outdated
Comment thread templates/base/head.tmpl
@silverwind
Copy link
Copy Markdown
Member

Why these nonce="not-needed"?

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Why these nonce="not-needed"?

Dummy attribute.

Then you grep the whole code base (or AI greps), find a lot of <script, you can know no tag nonce is missing. So it is pretty good for code maintenance.

@silverwind
Copy link
Copy Markdown
Member

Dummy attribute.

Will browsers ignore such invalid values or could it cause issues?

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Dummy attribute.

Will browsers ignore such invalid values or could it cause issues?

When no CSP, the nonce attribute does nothing.

@silverwind
Copy link
Copy Markdown
Member

silverwind commented Apr 15, 2026

Fine if it's not causing issues, but I'd feel more comfortable if this would be a data-nonce="not-needed" indicator, or even better, just a comment.

@silverwind
Copy link
Copy Markdown
Member

One must-fix from a review pass:

FastCryptoRandomBytes shares an unsynchronized rand/v2.ChaCha8 across goroutines (modules/util/util.go)

rand/v2.ChaCha8 is not documented as concurrency-safe, and its Read/Uint64 methods mutate internal state. The single instance returned by sync.OnceValue will be called concurrently by every HTTP handler rendering a page — sync.OnceValue only protects initialization, not subsequent Read calls. The race detector should flag this; in the worst case state corruption could lead to nonce collisions across requests.

Options:

  • Wrap access in a sync.Mutex
  • Use a sync.Pool of *ChaCha8 instances
  • Just call crypto/rand.Read directly — generating a 16-byte nonce once per page render is not a hot path that needs a 20x speedup

Also worth considering while touching this area:

  • _, _ = rand.Read(buf[:]) at seed time silently discards the error. If entropy is unavailable, the seed is all-zero and the generator is deterministic. panic on error would match the new style of CryptoRandomBytes right above it.
  • CryptoRandomBytes was changed to panic on error but still returns (..., error) where the error is always nil. Until the signature change lands, callers designed to handle the error now crash the process instead — better to keep the old behavior until the real refactor.

This comment was written with the help of Claude Opus 4.6.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

FastCryptoRandomBytes shares an unsynchronized rand/v2.ChaCha8 across goroutines (modules/util/util.go)

Done in 1dbf6f9

Fine if it's not causing issues, but I'd feel more comfortable if this would be a data-nonce="not-needed" indicator, or even better, just a comment.

Changed it to <script nonce src="...">, I think it reads better.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

wxiaoguang commented Apr 15, 2026

Also worth considering while touching this area:

* `_, _ = rand.Read(buf[:])` at seed time silently discards the error. If entropy is unavailable, the seed is all-zero

Hallucination, ChaCha8 never depends on entropy, it only does math calculation, never fails

And rand.Read also guarantees that it never fails.

image

@silverwind
Copy link
Copy Markdown
Member

silverwind commented Apr 15, 2026

Aggregated review pass with two models (Claude Opus 4.6 + GPT-5.4). Findings where they converged or diverged:

  1. CSP script-src * 'nonce-X' is too permissive (both) — services/context/context_template.go:HeadMetaContentSecurityPolicy. The * wildcard still allows any external script via src=. The nonce only blocks inline XSS; it does NOT prevent injected <script src="evil.com/x.js">. Comment "allow all by default (the same as old releases)" is honest about the trade-off, but users may expect strict CSP. Worth documenting what this protects (inline XSS, javascript: URIs) vs doesn't, or a follow-up with strict-dynamic/allowlist.

  2. CryptoRandomBytes now panics but keeps (…, error) signature (both) — modules/util/util.go:88. Callers in models/auth/access_token.go, models/auth/oauth2.go (2x), models/user/user.go, models/auth/twofactor.go, services/auth/auth_token.go, models/actions/utils.go have recoverable error paths that are now unreachable — an unexpected rand.Read failure crashes the process. Either keep return buf, err, or remove the error in this PR and fix all callers.

  3. math/rand/v2.ChaCha8 for CSP nonces (GPT-5.4: Critical; Opus 4.6: Low) — ChaCha8 is documented CSPRNG, seeded with 32 bytes from crypto/rand, so technically fine. But the math/rand/v2 package doc warns against security-sensitive use, which is a confusing signal. A comment on chaCha8RandPool clarifying "ChaCha8 is CSPRNG-safe, used for security-sensitive nonces" would prevent a future refactor from swapping it out.

  4. <script nonce> with no value in modules/markup/render.go and modules/markup/external/openapi.go (GPT-5.4) — Creates an empty nonce="" attribute. Works as a grep marker by design per earlier comments, but if these iframe responses ever inherit a script-src 'nonce-X' CSP, scripts silently break.

  5. executeScripts nonce propagation is fragile (both) — web_src/js/features/repo-issue-pull.ts:71 uses document.querySelector('script[nonce]')!.getAttribute('nonce')!. Per WHATWG spec, header-delivered CSP scrubs the attribute value to empty string (only .nonce IDL property retains it). Meta-CSP as used here doesn't trigger scrubbing, but adding a CSP header later silently breaks this. Prefer element.nonce. Also: double ! crashes if no nonce-bearing script exists, and if querySelector matches an empty-nonce script from (4), cloned scripts get empty nonce.

  6. TemplateContext mutation is unsynchronized (GPT-5.4) — CspScriptNonce writes to a map[string]any without a lock. Today rendering is single-goroutine per request so no real race, but a dedicated struct field would be safer than a map entry if html/template ever parallelizes.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Aggregated review pass with two models (Claude Opus 4.6 + GPT-5.4). Findings where they converged or diverged:

I have no interest to read.

If you think anything is a must, please point out directly

@silverwind
Copy link
Copy Markdown
Member

silverwind commented Apr 15, 2026

Those are high-quality reviews, you should at minimum comment them. Point 3 is likely a dupe as per discussion above. Why do you even request Copilot review when you don't want to read AI reviews?

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Those are high-quality reviews, you should at minimum comment them. Point 3 is likely a dupe as per discussion above. Why do you even request Copilot review when you don't want to read AI reviews?

That's just bullshit. AI slop

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Why do you even request Copilot review when you don't want to read AI reviews?

I ask AI to help to write code and review, I address my problems. I always hide the unrelated review, never waste reviewers' or author's time.

As the document said: you should post your thoughts, but not copy paste AI slop.

You are just keeping pasting AI slop, why I don't just ask my AI to review and waste time on answering your AI's bullshit?

@silverwind
Copy link
Copy Markdown
Member

Ok with me to dismiss all except the * defaults.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Ok with me to dismiss all except the * defaults.

If you don't like it, show your plan, show feasible and actionable "TODOs".

Don't make nonsense comment with your guess/imagination.

@silverwind
Copy link
Copy Markdown
Member

silverwind commented Apr 15, 2026

If you don't like it, do you really know a plan to improve it without breaking existing users?

Plan is to set self in this PR, call out the breaking change in the release notes for v1.27 so that users who run external scripts must adapt the CSP setting. As I understand you want to defer the self change to later. That may also be fine but why not do it now?

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

If you don't like it, do you really know a plan to improve it without breaking existing users?

Plan is to set self in this PR, call out the breaking change in the release notes for v1.27 so that users who run external scripts must adapt the CSP setting. As I understand you want to defer the self change to later. That may also be fine but why not do it now?

When you write and review other PRs, haven't you seen URLs like STATIC_URL_PREFIX?

You also ever said that "AssetURI" is a good name because it can be a full URL, not related to this origin.

I don't see any easy plan to make it completely right for end users.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

wxiaoguang commented Apr 15, 2026

Plan is to set self in this PR,

It is never the plan. It will also break a lot of users who use custom templates or external renders.

I believe you should remember this:

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

It's not all bullshit. Just filter it. The insecure * is definitely a concern I also have, it defeats the whole CSP.

I think I have answered everything in #37232 (comment).

These reviews are all bullshit.

@silverwind
Copy link
Copy Markdown
Member

silverwind commented Apr 15, 2026

We should at least give users a opt-in ini setting so they can configure self. I'd definitely do that on my instances because I don't host 3rd-party resources and I don't want XSS. I'm already setting a self CSP via nginx but that setting would benefit users without nginx.

Do you not see the huge security benefit self would provide?

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

Do you not see the huge security benefit self would provide?

Why you assume that I don't see?

I have explained everything in a0c5b94

Does this PR make anything worse? If no, I don't think src self is in this PR's scope.

@wxiaoguang
Copy link
Copy Markdown
Contributor Author

And, isn't this PR's purpose quite clear?

image

Copy link
Copy Markdown
Member

@silverwind silverwind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an improvement and it won't break stuff, but we definitely should make * configurable later.

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Apr 15, 2026
@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Apr 15, 2026
@wxiaoguang wxiaoguang enabled auto-merge (squash) April 15, 2026 19:37
@wxiaoguang wxiaoguang merged commit 82bfde2 into go-gitea:main Apr 15, 2026
26 checks passed
@GiteaBot GiteaBot added this to the 1.27.0 milestone Apr 15, 2026
@wxiaoguang wxiaoguang deleted the enable-csp-nonce branch April 15, 2026 20:20
@silverwind
Copy link
Copy Markdown
Member

Some followup ideas in #37238.

wxiaoguang added a commit to wxiaoguang/gitea that referenced this pull request Apr 16, 2026
@wxiaoguang wxiaoguang added the pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it! label Apr 16, 2026
zjjhot added a commit to zjjhot/gitea that referenced this pull request Apr 17, 2026
* main:
  Replace `dropzone` with `@deltablot/dropzone` (go-gitea#37237)
  Add `ExternalIDClaim` option for OAuth2 OIDC auth source (go-gitea#37229)
  Remove error returns from crypto random helpers and callers (go-gitea#37240)
  Use Content-Security-Policy: script nonce (go-gitea#37232)
  Remove htmx (go-gitea#37224)
  Refactor "htmx" to "fetch action" (go-gitea#37208)
  Fix UI regression (go-gitea#37218)
  Fix corrupted JSON caused by goccy library (go-gitea#37214)
  Add test for "fetch redirect", add CSS value validation for external render (go-gitea#37207)
  Fix incorrect concurrency check (go-gitea#37205)
  refactor: simplify ParseCatFileTreeLine and catBatchParseTreeEntries (go-gitea#37210)
  Update go js py dependencies (go-gitea#37204)
@silverwind
Copy link
Copy Markdown
Member

Regression: #37257

silverwind added a commit to silverwind/gitea that referenced this pull request Apr 19, 2026
asciinema-player uses WebAssembly, which is blocked by the main site
CSP introduced in go-gitea#37232 (script-src lacks 'wasm-unsafe-eval'). Moving
the renderer into an iframe does not help on its own because srcdoc
iframes inherit the parent CSP per CSP3 §4.2.3.6.

Two changes:
- add 'wasm-unsafe-eval' to script-src so srcdoc iframes can load WASM
- convert the asciicast renderer to the existing frontendRenderer
  iframe pattern (same as 3D viewer and openapi-swagger) for
  consistency and isolation

Fixes go-gitea#37257

Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Content-Security-Policy

5 participants