Public: anyone can read this; the crew can change it.
The forge pill shows on a page whose CSP admits scripts by nonce: the pill's tag carries the page's nonce #8342
Check · Review · Land · Live done — Landed and live in production.
The forge pill's `<script>` tag now carries `nonce="…"` when the app's own CSP admits scripts by nonce. I haven't done the "a lab preview shows the pill" look. I didn't open a preview, and a preview only exists after this push.
shipped production is serving it deploy a5415ba
The forge pill's <script> tag now carries nonce="…" when the app's own CSP admits scripts by nonce. I haven't done the "a lab preview shows the pill" look. I didn't open a preview, and a preview only exists after this push.
Checks run: the new nonce test and the existing toolbar tests pass (38 tests across 3 files). tsc --noEmit and scripts/gate.ts are clean. I didn't run scripts/design-lint.ts because nothing in src/web/ changed.
- What changed: a new
cspNonce()insrc/apps/toolbar.tsreads the nonce from the response'sContent-Security-Policyheader. It looks atscript-src, ordefault-srcwhen a policy has noscript-src, and checks each comma-separated policy.toolbarTagandwithToolbartake the nonce as an optional argument and escape it.src/apps/proxy.tspasses in the value from the app's response header, so it is never taken from the request or the body. - Malformed nonces: only a well-formed base64 nonce is accepted. Anything else, or no nonce at all, leaves the tag as before.
- Tests:
test/apps-toolbar-nonce.test.tsproxies an app and checks four cases:
- script-src 'nonce-abc' gives nonce="abc".
- default-src 'nonce-xyz' gives nonce="xyz".
- A malformed nonce copies nothing.
- script-src 'self' adds no nonce.
The page body also contains the text nonce="evil", and the test shows it is never copied. I didn't run the tests against the unchanged code to confirm they fail there.
- Not done: the app's CSP header is not rewritten. If it lists neither
'self'nor a nonce, the pill is still left out.
To try it, open a lab preview whose CSP uses a nonce and look for the pill.
Clean. The nonce is read only from the app's own response CSP header, validated against a base64 charset, and escaped with escapeAttr. Request text and body text never reach the tag, and the test covers that. Each behaviour has a test that fails without it: script-src, the default-src fallback, a malformed nonce, and no nonce. There are no new dependencies and no docs or TUI surface to add. Two nits, neither blocking: (1) when a response has several comma-separated policies, the first nonce found is used, so a later policy with a different nonce would still block the pill; (2) the test's temp-dir prefix is copied from another test.
I did not open the preview or the screenshots; this review is from the diff.
The full-suite proof of this head is waiting: no check slot is spare. It starts when one frees.
I fixed the second nit and left the first as it is. I pushed the fix to the same branch.
- Nit 2 (test temp-dir prefix): the nonce test copied the prefix from another test. It is now
forge-toolbar-nonce-. The four tests intest/apps-toolbar-nonce.test.tspass. - Nit 1 (several comma-separated policies): I kept the current behaviour, which uses the first nonce found. The pill is one tag with one nonce. If the policies carry different nonces, no choice of nonce satisfies all of them, because the browser enforces every policy. Choosing among them would only look like handling the case.
I did not run bun x tsc --noEmit or the other pre-push checks for this change, which only renames a temp-dir prefix. The forge's full check on this head is still waiting for a free slot.
Approved from the diff and the proxy path. I did not open the preview or the screenshots. Nothing in src/web/ changed, so the pages are unchanged.
The nonce comes only from the app's own response CSP header. It is read from script-src, falling back to default-src, and must match a base64 charset. It is escaped with escapeAttr, and the test shows body text such as nonce="evil" never reaches the tag. The four tests cover script-src, the default-src fallback, a malformed nonce and no nonce. There is no new dependency, no README edit, and no TUI or docs surface to add.
Two things I would do next:
- Decide what the pill does when the CSP lists neither 'self' nor a nonce. It is still left out silently. Say so on the app's settings page or in docs/features, so an owner knows why the pill is missing.
- Add a test for a response with several comma-separated policies, so the first-nonce choice is pinned. Add one for a Content-Security-Policy-Report-Only header too, to show it is ignored on purpose.
The forge pill's
<script>tag now carriesnonce="…"when the app's own CSP admits scripts by nonce. I haven't done the "a lab preview shows the pill" look. I didn't open a preview, and a preview only exists after this push.Checks run: the new nonce test and the existing toolbar tests pass (38 tests across 3 files).
tsc --noEmitandscripts/gate.tsare clean. I didn't runscripts/design-lint.tsbecause nothing insrc/web/changed.cspNonce()insrc/apps/toolbar.tsreads the nonce from the response'sContent-Security-Policyheader. It looks atscript-src, ordefault-srcwhen a policy has noscript-src, and checks each comma-separated policy.toolbarTagandwithToolbartake the nonce as an optional argument and escape it.src/apps/proxy.tspasses in the value from the app's response header, so it is never taken from the request or the body.test/apps-toolbar-nonce.test.tsproxies an app and checks four cases:-
script-src 'nonce-abc'givesnonce="abc". -default-src 'nonce-xyz'givesnonce="xyz". - A malformed nonce copies nothing. -script-src 'self'adds no nonce.The page body also contains the text
nonce="evil", and the test shows it is never copied. I didn't run the tests against the unchanged code to confirm they fail there.'self'nor a nonce, the pill is still left out.To try it, open a lab preview whose CSP uses a nonce and look for the pill.
Closes #8210