Skip to content

build: WALLET-1364 — emit the CSP nonce only for Chrome production builds - #1429

Open
ost-ptk wants to merge 1 commit into
release/2.7.0from
WALLET-1364-csp-nonce
Open

build: WALLET-1364 — emit the CSP nonce only for Chrome production builds#1429
ost-ptk wants to merge 1 commit into
release/2.7.0from
WALLET-1364-csp-nonce

Conversation

@ost-ptk

@ost-ptk ost-ptk commented Jul 28, 2026

Copy link
Copy Markdown
Member

Ticket: https://make-software.atlassian.net/browse/WALLET-1364 (P1 item 4)

The problem

webpack.config.js generated a random CSP nonce per build and injected it through DefinePlugin for every browser target, even though only the Chrome-production CSP arm pins style-src to it. Both readers — src/set-webpack-nonce.ts (webpack's __webpack_nonce__, read by style-loader) and src/libs/ui/style-sheet-manager.tsx (styled-components) — are browser-agnostic, so every Firefox and Safari bundle carried a fresh random base64 literal that nothing ever used.

AMO source review rebuilds the submitted source and compares the result to the uploaded artifact byte for byte. A per-build random literal makes that impossible.

The fix

The value is generated only when isChrome && !isDev; every other target gets null.

The DefinePlugin key stays defined for all targets on purpose. Nothing else defines process in these bundles, so dropping the key would leave process.env.CSP_NONCE unreplaced in the output and throw ReferenceError: process is not defined at runtime. Only the value changes.

Both readers skip the attribute entirely rather than stamping a bogus one — verified against the installed library sources, not assumed: style-loader does if (nonce) setAttribute("nonce", nonce), and StyleSheetManager types the prop as nonce?: undefined | string, so null ?? undefined is the correct falsy shape.

Deriving the nonce from the commit hash was considered and rejected: a publicly computable nonce adds nothing over a static one, whereas gating removes the value from the non-Chrome bundles altogether.

Verification

  • Two consecutive npm run build:firefox runs are byte-identical after this change and differed before it — the difference was confined to exactly the five bundles that prepend the nonce setter, which matches the entry wiring.
  • build/chrome/manifest.json still carries a real nonce-<base64> in its CSP, and the Chrome bundle still sets it at runtime.
  • No null/undefined string leaked into the Firefox output where the nonce used to be.
  • npm run ci-check passes.

The nonce was generated per build and injected via DefinePlugin for every
browser target, although only the Chrome-production CSP arm pins style-src to
it. Both readers are browser-agnostic, so every Firefox and Safari bundle
carried a fresh random base64 literal that nothing used — and AMO source review
rebuilds the submitted source and compares the artifact byte for byte, which a
per-build random literal makes impossible.

The DefinePlugin key stays defined for all targets on purpose: nothing else
defines `process` in these bundles, so dropping the key would leave
process.env.CSP_NONCE unreplaced and throw ReferenceError at runtime. Only the
value becomes null off the Chrome-production path, and both readers now skip the
attribute entirely rather than stamping nonce="null".

Deriving the nonce from the commit hash was considered and rejected: a publicly
computable nonce adds nothing over a static one, while gating removes the value
from the non-Chrome bundles altogether.

Verified: two consecutive build:firefox runs are byte-identical after this
change and differed before it; build/chrome still carries a real nonce in its
manifest CSP.
@ost-ptk
ost-ptk requested a review from Comp0te July 29, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant