[Security Audit] Missing Content-Security-Policy header #5
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: LOW
Description
The application does not set a Content-Security-Policy (CSP) header on any responses, leaving it without defense-in-depth against XSS and content injection attacks.
Affected File
Context
This finding is elevated in importance because issue #3 (Stored XSS in stats dashboard) demonstrates a live XSS vector. A properly configured CSP would have blocked script execution even with the unescaped HTML output.
Fix
Add a Content-Security-Policy header via Caddyfile or in filter_response_headers(). Recommended starting CSP for a reverse proxy mirror:
The 'unsafe-inline' allowances are needed for inline Chart.js configuration on the stats page; these can be removed if the stats page is refactored to use nonce-based CSP.
Auto-Triage Report for Issue #5
Status: Open
Existing Labels: enhancement (correct)
Assignee: @akiba (newly assigned)
Summary: [Security Audit] Missing Content-Security-Policy header. Severity: LOW.
Assessment: Valid enhancement finding. While LOW severity alone, it is elevated in importance because Issue #3 (Stored XSS) demonstrates a live XSS vector that CSP would have blocked. A properly configured CSP provides critical defense-in-depth.
Next Steps:
Triage Summary
Status: 🟢 Confirmed — Missing CSP header
Severity: LOW (medium when combined with #3)
Priority: 🟠 High — defense-in-depth for XSS protection
Assessment
This is a valid security finding. The application does not set a Content-Security-Policy header anywhere (not in
filter_response_headers()in app.py, nor in the Caddyfile). While low severity on its own, this finding is elevated because issue #3 (Stored XSS) demonstrates a live XSS vector — a properly configured CSP would have blocked script execution even with the unescaped HTML output.Recommended Next Steps
filter_response_headers()default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://archiveofourown.orgTriage Actions Taken
enhancement(already set) ✅🤖 Triage Summary
Classification: Enhancement / Security (LOW severity, but HIGH value)
Labels: enhancement ✅
Assignee: akiba ✅
Assessment: The application lacks Content-Security-Policy headers entirely, removing an important layer of defense-in-depth. While LOW severity on its own, this issue is directly related to #3 (Stored XSS) — a properly configured CSP would have blocked script execution even with the unescaped HTML output.
Recommended Next Steps:
🔄 Follow-up Triage (2026-06-30)
Label corrected:
enhancement→bug— a missing security header is a defect, not a feature request. This issue provides defense-in-depth for the XSS vector in #3.Status: Still open. Recommended fix: add CSP header in Caddyfile or
filter_response_headers(). No new blockers observed.🤖 Triage Summary
Status: Confirmed — legitimate LOW severity security finding.
Labels: bug ✅
Assignee: akiba ✅
Next Steps
Caddyfile(recommended — easier to maintain) or inapp.py:filter_response_headers()unsafe-inlineallowancesSeverity Assessment
Correctly assessed as Low as a standalone finding, but elevated in importance as a compensating control for issue #3 (Stored XSS). Recommend implementing alongside the XSS fix.
Triage Assessment: LOW severity
This is a valid security audit finding. The application is missing a CSP header, which would provide defense-in-depth against XSS (especially relevant given issue #3).
Labels: bug ✅
Assignee: akiba ✅
Recommended next steps:
Content-Security-Policyheader inapp.py:filter_response_headers()FIXED — Commit
122c408Fix: Added
Content-Security-Policyheader to both site blocks in Caddyfile:This provides defense-in-depth for XSS (including the vector fixed in #3). Allows Chart.js from CDN, inline styles for stats dashboard, but blocks inline scripts, foreign frames, and plugins.
Deployed via start.sh — Caddy reloaded, workers restarted.