You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
web: serve a per-request CSP nonce so MUI's Emotion styles apply under style-src 'self' #873
The production Content Security Policy is style-src 'self' (src/Cluckwork.Api/Security/SecurityHeaders.cs). MUI styles through Emotion, which injects a <style data-emotion> element at runtime, and the browser refuses it under that policy. Measured on the sim harness at 390: the style element is in the document, html computes box-sizing: content-box where CssBaseline sets border-box, /daily-entry lays out 419px wide in a 390px frame, and the console reports "Applying inline style violates the following Content Security Policy directive style-src 'self'". The same path carries every sx, styled() and styleOverrides value, so no MUI styling reaches the screen until this is settled. It was invisible until #871 because no MUI component rendered before it.
The decision (owner, 2026-09-14): a per-request nonce, policy stays strict
Rejected: 'unsafe-inline' on style-src (re-opens CSS injection, including attribute-selector exfiltration; a security regression the record would have to own) and build-time CSS extraction (fights the runtime palette-from-the-document design in docs/decisions/674-ui-component-library.md).
SPA.FarmThemeProvider wraps its children in Emotion's CacheProvider with a cache created with that nonce (createCache({ key, nonce })), read once from the meta tag at boot. No other code reads the nonce.
Tests. An integration test that the served index.html's nonce equals the header's and differs between two requests; a unit test that the cache is built with the meta nonce and that a missing meta falls back to no nonce in Development only (Production fails closed, the header would block styles anyway); a Playwright assertion on the sim harness that a rendered MUI component's computed style is applied under the real CSP (the console must carry no CSP violation). SecurityHeadersTests (or the equivalent) updated with the new directive shape.
A rendered MUI Button on the sim harness (Production CSP) shows its themed background with no CSP violation in the console, proved by the Playwright assertion above.
The nonce differs per request and the served page's meta equals the header, proved by the integration test.
Before/after screenshots at 1280 and 390 of one screen with an MUI component under the real CSP, captured from a stack rebuilt at the head under review.
Constraints inherited
Strict CSP is a deliberate property of this deployment (see the header comments in SecurityHeaders.cs and #141); script-src 'self' and the external pre-paint script are untouched. The nonce must come from a cryptographic RNG and never be logged. index.html remains cacheable only as it is today.
Part of #674. Groundwork, sequenced before #864's overrides and every screen slice.
The finding (from #871, 2026-09-14)
The production Content Security Policy is
style-src 'self'(src/Cluckwork.Api/Security/SecurityHeaders.cs). MUI styles through Emotion, which injects a<style data-emotion>element at runtime, and the browser refuses it under that policy. Measured on the sim harness at 390: the style element is in the document,htmlcomputesbox-sizing: content-boxwhereCssBaselinesetsborder-box,/daily-entrylays out 419px wide in a 390px frame, and the console reports "Applying inline style violates the following Content Security Policy directivestyle-src 'self'". The same path carries everysx,styled()andstyleOverridesvalue, so no MUI styling reaches the screen until this is settled. It was invisible until #871 because no MUI component rendered before it.The decision (owner, 2026-09-14): a per-request nonce, policy stays strict
Rejected:
'unsafe-inline'onstyle-src(re-opens CSS injection, including attribute-selector exfiltration; a security regression the record would have to own) and build-time CSS extraction (fights the runtime palette-from-the-document design indocs/decisions/674-ui-component-library.md).Scope
SecurityHeadersmints a nonce per response and emitsstyle-src 'self' 'nonce-<value>'(keep'self'sostyles.cssstill loads). The SPA'sindex.htmlis served as a small templated response that carries the same nonce in a<meta name="csp-nonce" content="...">(or equivalent), instead of a static file fromwwwroot; hashed/assets/*stay static and immutable (Static asset Cache-Control headers + Cloudflare-in-front CDN strategy #141). The revalidation semanticsindex.htmlhas today must survive (it is the update-detection path for the PWA, Installable PWA baseline — manifest, icons, service worker app-shell cache (split from #50) #142).FarmThemeProviderwraps its children in Emotion'sCacheProviderwith a cache created with that nonce (createCache({ key, nonce })), read once from the meta tag at boot. No other code reads the nonce.index.html's nonce equals the header's and differs between two requests; a unit test that the cache is built with the meta nonce and that a missing meta falls back to no nonce in Development only (Production fails closed, the header would block styles anyway); a Playwright assertion on the sim harness that a rendered MUI component's computed style is applied under the real CSP (the console must carry no CSP violation).SecurityHeadersTests(or the equivalent) updated with the new directive shape.docs/decisions/674-ui-component-library.mdgains an amendment recording the finding and this answer;docs/architecture.mdif it draws the SPA-serving path;AGENTS.mdonly if a rule changes (none expected). Sim harness (#243) rotted silently: 4 breakages, no CI ever ran it #370:SecurityHeadersis code, not configuration, so the sim harness needs no new key; say so in the PR. Developer experience: add an Aspire AppHost for local orchestration and observability #565: same for the AppHost.Done when
Buttonon the sim harness (Production CSP) shows its themed background with no CSP violation in the console, proved by the Playwright assertion above.Constraints inherited
Strict CSP is a deliberate property of this deployment (see the header comments in
SecurityHeaders.csand #141);script-src 'self'and the external pre-paint script are untouched. The nonce must come from a cryptographic RNG and never be logged.index.htmlremains cacheable only as it is today.