Problem
The SecurityHeadersMiddleware in vibetuner/frontend/middleware.py emits Content-Security-Policy-Report-Only when settings.debug is true and Content-Security-Policy otherwise. The split is helpful for triage, but it means CSP-blocking bugs are systematically invisible during local development and only surface in production.
We just hit this again on a downstream app: an HTMX-loaded modal contained an inline <script> block whose nonce (the per-request nonce of the fragment response) didn't match the parent page's CSP nonce. In dev, the browser logged the violation and ran the script anyway; in production, it was blocked silently and the modal was broken.
The conventions doc on the downstream side already says "treat reported CSP violations in dev as failures", but in practice nothing forces that — the page keeps working, and the violation lives only in the console.
Request
Add a setting (e.g. SECURITY_HEADERS_ENFORCE_CSP_IN_DEBUG) that controls whether settings.debug swaps the CSP header to report-only. Default it to True (i.e. enforce CSP even in debug) so newly-introduced CSP violations actually break the page locally and are caught before they ship. Teams that prefer the existing soft mode can opt out via env var.
Concretely, replace:
csp_header = (
"Content-Security-Policy-Report-Only"
if settings.debug
else "Content-Security-Policy"
)
with something like:
report_only = settings.debug and not settings.security_headers.enforce_csp_in_debug
csp_header = (
"Content-Security-Policy-Report-Only"
if report_only
else "Content-Security-Policy"
)
and add the enforce_csp_in_debug: bool = True field to the security_headers config.
Why default to enforce
- "Works locally, breaks in prod" CSP bugs are a recurring class of incident on apps using vibetuner.
- Enforcing locally makes the dev/prod gap explicit at write time, not at deploy time.
- The current report-only behavior remains available behind a flag for anyone who wants it.
Filed by Claude Code.
Problem
The
SecurityHeadersMiddlewareinvibetuner/frontend/middleware.pyemitsContent-Security-Policy-Report-Onlywhensettings.debugis true andContent-Security-Policyotherwise. The split is helpful for triage, but it means CSP-blocking bugs are systematically invisible during local development and only surface in production.We just hit this again on a downstream app: an HTMX-loaded modal contained an inline
<script>block whose nonce (the per-request nonce of the fragment response) didn't match the parent page's CSP nonce. In dev, the browser logged the violation and ran the script anyway; in production, it was blocked silently and the modal was broken.The conventions doc on the downstream side already says "treat reported CSP violations in dev as failures", but in practice nothing forces that — the page keeps working, and the violation lives only in the console.
Request
Add a setting (e.g.
SECURITY_HEADERS_ENFORCE_CSP_IN_DEBUG) that controls whethersettings.debugswaps the CSP header to report-only. Default it toTrue(i.e. enforce CSP even in debug) so newly-introduced CSP violations actually break the page locally and are caught before they ship. Teams that prefer the existing soft mode can opt out via env var.Concretely, replace:
with something like:
and add the
enforce_csp_in_debug: bool = Truefield to thesecurity_headersconfig.Why default to enforce
Filed by Claude Code.