Skip to content

fix: replace unsafe-inline CSP with per-request nonce#783

Open
rager306 wants to merge 1 commit intoRightNow-AI:mainfrom
rager306:fix/csp-nonce
Open

fix: replace unsafe-inline CSP with per-request nonce#783
rager306 wants to merge 1 commit intoRightNow-AI:mainfrom
rager306:fix/csp-nonce

Conversation

@rager306
Copy link

Summary

The dashboard Content-Security-Policy currently allows 'unsafe-inline' for script-src:

script-src 'self' 'unsafe-inline' 'unsafe-eval'

'unsafe-inline' permits any inline <script> block to execute, including attacker-injected scripts. If any part of the dashboard reflects user-controlled data — agent names, message content, channel descriptions, persona fields — an attacker can store a payload that executes JavaScript in the admin's browser (stored XSS).

Fix

Generate a cryptographic nonce (uuid::Uuid::new_v4()) on every dashboard request. All <script> tags carry nonce="__NONCE__" at compile time; the nonce is substituted at response time. The CSP becomes:

script-src 'self' 'nonce-{random}' 'unsafe-eval'

('unsafe-eval' is retained — required for Alpine.js x-data expression evaluation.)

API endpoints receive a separate strict policy: default-src 'none'; frame-ancestors 'none'.

'unsafe-inline' is fully removed. No new dependencies — uuid is already a workspace dependency.

Testing

  • cargo build -p openfang-api passes
  • Dashboard loads and all scripts execute correctly (nonce injected at runtime)
  • 'unsafe-inline' removed from CSP
  • Each page load generates a unique nonce (no caching of nonce across requests)

The dashboard CSP uses 'unsafe-inline' for script-src, which permits
any inline <script> block to execute — including attacker-injected
scripts if any endpoint reflects user input (agent names, message
content, channel descriptions, etc.).

Replace with a per-request cryptographic nonce (UUID v4):
- webchat_page generates a unique nonce on every request
- All <script> tags embed the nonce at compile time via __NONCE__ placeholder
- CSP becomes: script-src 'self' 'nonce-{nonce}' 'unsafe-eval'
  ('unsafe-eval' is still required for Alpine.js x-data expressions)
- API endpoints receive a strict default-src 'none'; frame-ancestors 'none'
  policy instead of the permissive dashboard policy

This is a standard CSP Level 2 hardening; all modern browsers support nonces.
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