Files
crewli/deploy/nginx/csp-spa.conf
bert.hausmans 940297f214 security: implement CSP headers (API middleware + Nginx configs + dev meta tags)
API middleware:
- SecurityHeaders now sets Content-Security-Policy from config/security.php
- Default API policy: "default-src 'none'; frame-ancestors 'none'"
- Supports report-only mode via CSP_REPORT_ONLY env var
- Policy value configurable via CSP_POLICY env var

Nginx deployment configs (deploy/nginx/):
- security-headers.conf: shared headers for all server blocks
- csp-api.conf: restrictive JSON-only policy for api.crewli.app
- csp-spa.conf: SPA policy for app/admin (self + unsafe-inline styles)
- csp-portal.conf: portal policy matching SPA

Development:
- CSP meta tags added to all three index.html files
- Includes 'unsafe-inline' + 'unsafe-eval' for Vite HMR/loader script
- Each app allows its own ws:// port for HMR websocket

Resolves security finding A13-9.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:14:37 +02:00

16 lines
1.1 KiB
Plaintext

# CSP for app.crewli.app and admin.crewli.app
# Vite bundles all JS/CSS into same-origin files.
# 'unsafe-inline' for style-src is required by Vuetify (inline styles for theming).
# img-src https: allows organisation logos loaded from external URLs.
# connect-src must include the API domain for XHR/fetch calls.
#
# IMPORTANT: Start with Content-Security-Policy-Report-Only to catch
# false positives. Switch to Content-Security-Policy after 1-2 weeks
# of clean logs.
# Report-only mode (start with this):
# add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://api.crewli.app; frame-ancestors 'none'; form-action 'self'; base-uri 'self'" always;
# Enforce mode (switch to this after testing):
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://api.crewli.app; frame-ancestors 'none'; form-action 'self'; base-uri 'self'" always;