Skip to content

Feat: Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval' - #10153

Open
hiteshjambhale wants to merge 3 commits into
pgadmin-org:masterfrom
hiteshjambhale:feat/csp-nonce
Open

Feat: Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval'#10153
hiteshjambhale wants to merge 3 commits into
pgadmin-org:masterfrom
hiteshjambhale:feat/csp-nonce

Conversation

@hiteshjambhale

@hiteshjambhale hiteshjambhale commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

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 on flask.g so the same value is emitted both in the rendered templates and in the Content-Security-Policy response header. A {nonce} placeholder in CONTENT_SECURITY_POLICY is substituted at runtime.
  • __init__.py — expose csp_nonce to templates.
  • Templates (base.html + tool templates) — tag inline <script>/<style> with nonce="{{ csp_nonce }}", and set window.__webpack_nonce__ so webpack's dynamically injected assets carry the nonce too.
  • config.py — new default policy:
    default-src 'self' ws: http: data: blob:;
    script-src 'self' 'nonce-{nonce}';
    style-src 'self' 'unsafe-inline';
    

Notes / scope

  • 'unsafe-eval' is not needed by production bundles (verified). Development bundles use webpack's eval devtool and do need it, so devs add it in config_local.py (documented in config.py).
  • style-src keeps 'unsafe-inline' by necessity: MUI/React inject runtime <style> elements and, more importantly, inline style="" 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.
  • Backward compatible: if {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

  • Security Enhancements
    • Strengthened Content Security Policy to default to same-origin and removed permissive script allowances (including eval).
    • Added per-request CSP nonces and applied them to inline scripts/styles across the main interface and key tools (debugger, ERD, SQL editor, query pages, and schema diff).
    • Ensured the Content-Security-Policy response header is generated consistently by replacing the nonce placeholder at render time, including nonce support for the app’s script loader setup.

…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>
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 263cdde8-66fb-4f15-89a0-f512065d3026

📥 Commits

Reviewing files that changed from the base of the PR and between c6b44ab and 2ada061.

📒 Files selected for processing (1)
  • web/config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/config.py

Walkthrough

The 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.

Changes

Content Security Policy nonce support

Layer / File(s) Summary
Runtime CSP generation
web/config.py, web/pgadmin/utils/security_headers.py
The configured CSP uses {nonce}, while SecurityHeaders generates a request-scoped nonce, resolves the policy, and sets the response header.
Template nonce context and base assets
web/pgadmin/__init__.py, web/pgadmin/templates/base.html
The nonce is added to template context and applied to base inline scripts, styles, vendor bundles, webpack setup, and initialization scripts.
Tool template nonce coverage
web/pgadmin/templates/security/render_page.html, web/pgadmin/tools/*/templates/*
Inline style blocks in security, debugger, ERD, PSQL, schema diff, and SQL editor templates now use the request nonce.

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
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing per-request CSP nonces to replace unsafe inline/eval usage.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hiteshjambhale hiteshjambhale changed the title Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval' Feat: Use per-request nonce in CSP to remove 'unsafe-inline' / 'unsafe-eval' Jul 15, 2026
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.

2 participants