Feat: Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval'#10153
Open
hiteshjambhale wants to merge 2 commits into
Open
Feat: Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval'#10153hiteshjambhale wants to merge 2 commits into
hiteshjambhale wants to merge 2 commits into
Conversation
…al' (pgadmin-org#7599) Harden the default Content-Security-Policy so inline scripts run only via a per-request nonce instead of a blanket 'unsafe-inline', and drop 'unsafe-eval'. - Generate a per-request nonce (secrets.token_urlsafe) cached on flask.g so the exact same value is emitted in templates and the CSP response header. - Substitute a {nonce} placeholder in CONTENT_SECURITY_POLICY at runtime. - Tag inline <script>/<style> tags, and set window.__webpack_nonce__ so webpack's dynamically injected assets carry the nonce too. - New default: script-src 'self' 'nonce-{nonce}' (no 'unsafe-inline'/'unsafe-eval'). - style-src keeps 'unsafe-inline': MUI/React inject un-nonced runtime styles and inline style="" attributes that cannot be nonced. - 'unsafe-eval' is not needed by production bundles; the dev ('eval' devtool) bundles add it via config_local.py (documented in config.py). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WalkthroughThe CSP now restricts scripts to same-origin or nonce-authorized execution. A request-scoped nonce is generated, added to response headers, exposed to templates, and applied to inline and bundled scripts and styles. ChangesContent Security Policy nonce support
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant FlaskRequest
participant SecurityHeaders
participant TemplateRenderer
Browser->>FlaskRequest: request page
FlaskRequest->>SecurityHeaders: generate request nonce
SecurityHeaders-->>FlaskRequest: cache nonce in flask.g
FlaskRequest->>TemplateRenderer: provide csp_nonce
TemplateRenderer-->>Browser: render nonce-tagged scripts and styles
SecurityHeaders-->>Browser: send resolved CSP header
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses #7599 — removing
'unsafe-inline'and'unsafe-eval'from the Content-Security-Policy.This introduces a per-request CSP nonce so inline scripts run only when they carry the nonce, instead of relying on a blanket
'unsafe-inline'.'unsafe-eval'is dropped from the default policy as well.What changed
security_headers.py— generate a per-request nonce (secrets.token_urlsafe), cached onflask.gso the same value is emitted both in the rendered templates and in theContent-Security-Policyresponse header. A{nonce}placeholder inCONTENT_SECURITY_POLICYis substituted at runtime.__init__.py— exposecsp_nonceto templates.base.html+ tool templates) — tag inline<script>/<style>withnonce="{{ csp_nonce }}", and setwindow.__webpack_nonce__so webpack's dynamically injected assets carry the nonce too.config.py— new default policy:Notes / scope
'unsafe-eval'is not needed by production bundles (verified). Development bundles use webpack'sevaldevtool and do need it, so devs add it inconfig_local.py(documented inconfig.py).style-srckeeps'unsafe-inline'by necessity: MUI/React inject runtime<style>elements and, more importantly, inlinestyle=""attributes that cannot be covered by a nonce or hash. This matches standard practice for MUI/React apps — the meaningful XSS surface (scripts) is what gets locked down.{nonce}is absent from a custom policy, behaviour is unchanged.Testing
Verified against a production build with the strict policy: app loads, all tools (Query Tool, PSQL, ERD, Schema Diff, Debugger) work, and the JSON editor (ajv / jsonpath-plus) runs cleanly with no
'unsafe-eval'— confirming it can be dropped.Summary by CodeRabbit