[Security Audit] Missing Content-Security-Policy header #5

Closed
opened 2026-06-29 04:03:45 -05:00 by akiba · 7 comments
Owner

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

  • app.py: filter_response_headers() function — no CSP header added
  • No CSP in Caddyfile either

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:

Content-Security-Policy: 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.org;

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.

## 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 - app.py: filter_response_headers() function — no CSP header added - No CSP in Caddyfile either ### 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: ``` Content-Security-Policy: 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.org; ``` 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.
akiba added the enhancement label 2026-06-29 04:03:45 -05:00
akiba self-assigned this 2026-06-29 05:01:22 -05:00
Author
Owner

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:

  1. Add CSP header via Caddyfile or filter_response_headers()
  2. Use the recommended starting CSP from the issue body
  3. Refactor stats page to use nonce-based CSP to allow removing 'unsafe-inline' directives
  4. Verify CSP is present on all responses after deployment
## 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**: 1. Add CSP header via Caddyfile or filter_response_headers() 2. Use the recommended starting CSP from the issue body 3. Refactor stats page to use nonce-based CSP to allow removing 'unsafe-inline' directives 4. Verify CSP is present on all responses after deployment
Author
Owner

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.

  1. Add CSP header via Caddyfile or in filter_response_headers()
  2. Starting CSP: 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.org
  3. Consider refactoring stats page to use nonce-based CSP so 'unsafe-inline' can be removed
  4. Deploy alongside the fix for issue #3 for layered defense

Triage Actions Taken

  • Labels: enhancement (already set)
  • Assigned to: @akiba (already assigned)
  • Linked issue: #3 (Stored XSS) is the primary vulnerability this mitigates
## 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 1. Add CSP header via Caddyfile or in `filter_response_headers()` 2. Starting CSP: `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.org` 3. Consider refactoring stats page to use nonce-based CSP so 'unsafe-inline' can be removed 4. Deploy alongside the fix for issue #3 for layered defense ### Triage Actions Taken - Labels: `enhancement` (already set) ✅ - Assigned to: @akiba (already assigned) ✅ - Linked issue: #3 (Stored XSS) is the primary vulnerability this mitigates
Author
Owner

🤖 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:

  1. Add a CSP header either in the Caddyfile or in app.py's filter_response_headers()
  2. Recommended starting policy:
    Content-Security-Policy: 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.org;
    
  3. The 'unsafe-inline' allowances for Chart.js can be removed if the stats page is refactored to use nonce-based CSP
  4. Recommend resolving together with issue #3 to close both the XSS vector and the missing defense layer in one pass
## 🤖 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:** 1. Add a CSP header either in the Caddyfile or in app.py's filter_response_headers() 2. Recommended starting policy: ``` Content-Security-Policy: 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.org; ``` 3. The 'unsafe-inline' allowances for Chart.js can be removed if the stats page is refactored to use nonce-based CSP 4. **Recommend resolving together with issue #3** to close both the XSS vector and the missing defense layer in one pass
akiba added bug and removed enhancement labels 2026-06-30 00:01:56 -05:00
Author
Owner

🔄 Follow-up Triage (2026-06-30)

Label corrected: enhancementbug — 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.

## 🔄 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.
Author
Owner

🤖 Triage Summary

Status: Confirmed — legitimate LOW severity security finding.
Labels: bug
Assignee: akiba

Next Steps

  1. Add CSP header either via Caddyfile (recommended — easier to maintain) or in app.py:filter_response_headers()
  2. Use the suggested CSP policy from the issue description as a starting point
  3. If issue #3 (stored XSS) is fixed first, consider tightening CSP further by removing unsafe-inline allowances

Severity 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 Summary **Status:** Confirmed — legitimate LOW severity security finding. **Labels:** bug ✅ **Assignee:** akiba ✅ ### Next Steps 1. Add CSP header either via `Caddyfile` (recommended — easier to maintain) or in `app.py:filter_response_headers()` 2. Use the suggested CSP policy from the issue description as a starting point 3. If issue #3 (stored XSS) is fixed first, consider tightening CSP further by removing `unsafe-inline` allowances ### Severity 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.
Author
Owner

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:

  • Add Content-Security-Policy header in app.py:filter_response_headers()
  • Consider adding CSP directives to the Caddyfile as well
  • Reference the new header in the security documentation
  • Priority: LOW — implement after the HIGH and MEDIUM issues are resolved
**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:** - Add `Content-Security-Policy` header in `app.py:filter_response_headers()` - Consider adding CSP directives to the Caddyfile as well - Reference the new header in the security documentation - Priority: LOW — implement after the HIGH and MEDIUM issues are resolved
Author
Owner

FIXED — Commit 122c408

Fix: Added Content-Security-Policy header to both site blocks in Caddyfile:

Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; object-src 'none'"

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.

**FIXED** — Commit 122c408 Fix: Added `Content-Security-Policy` header to both site blocks in Caddyfile: ``` Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; object-src 'none'" ``` 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.
akiba closed this issue 2026-06-30 09:04:46 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: akiba/agento3#5