Brew a Content Security Policy
Turn real page loads into a CSP you can ship.
cspresso crawls up to N same‑origin pages with headless Chromium (Playwright), watches the assets that load,
and emits a draft Content-Security-Policy header.
--json
--evaluate
--bypass-csp
--include-sourcemaps
pipx install cspresso
cspresso https://example.com --max-pages 10
# visited: https://example.com/
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; ...;
Remember: it’s only a starting point: crawls may not hit every flow, and inline hashing/nonces require care.
Quickstart
cspresso https://mig5.net \
--ignore-non-html \
--max-pages 10
What you’ll get
A header line you can paste into your vhost, or parseable info with
--json
Tip: if an existing CSP might block loads during analysis, add
--bypass-csp.
# Evaluate a candidate CSP (Report-Only) and fail CI on violations
cspresso https://mig5.net \
--bypass-csp \
--evaluate "default-src 'self'; img-src 'none';" \
--json
{
[...]
"violations": [
{
"console": true,
"disposition": "report",
"documentURI": "https://mig5.net/",
"text": "Loading the image 'https://mig5.net/logo.svg' violates the following Content Security Policy directive: \"img-src 'none'\". The policy is report-only, so the violation has been logged but no further action has been taken.",
"type": "info"
},
{
"console": true,
"disposition": "report",
"documentURI": "https://mig5.net/static/mig5.asc",
"text": "Applying inline style violates the following Content Security Policy directive 'default-src 'self''. Either the 'unsafe-inline' keyword, a hash ('sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback. The policy is report-only, so the violation has been logged but no further action has been taken.",
"type": "info"
}
]
}
# exit code: 1 if violations detected
How it works
cspresso lets the browser do the hard part: execute the page, watch what it loads, and distill origins into directives.
Crawl
Visit up to
--max-pages same-origin pages and let the app’s JS run.Observe
Track scripts, styles, images, fonts, frames, and “connect-like” requests.
Draft a CSP
Emit a baseline policy plus observed origins per directive.
Evaluate
Inject a candidate as Report‑Only and capture violations with an exit code for CI.
Inline script/style is tricky: nonces must be generated per response, and hashes must match bytes exactly.
cspresso reports what it sees, but you should review and tighten before enforcing.
Popular flags
A few options that tend to matter in real deployments.
--bypass-csp
Strip existing CSP response headers so they don’t block discovery or evaluation.
--evaluate
Inject a candidate policy as Report‑Only and exit 1 if any violations are detected.
--include-sourcemaps
Heuristically discover sourcemap origins and add them to
connect-src.--upgrade-insecure-requests
Emit
upgrade-insecure-requests in the proposed policy.--browsers-path
Control where Playwright installs Chromium (handy for AppImage/CI caches).
--json
Machine-readable output: CSP, visited URLs, notes, and evaluation violations.
Install
pipx, pip, Poetry, or a standalone AppImage from Releases.
# Recommended
pipx install cspresso
# Or plain pip (use a venv)
pip install cspresso
Playwright browsers
cspresso can auto-install Chromium for Playwright if it isn’t present. By default it installs into
./.pw-browsers
for deterministic builds and easy CI caching.
Override with
--browsers-path or PLAYWRIGHT_BROWSERS_PATH.
poetry add cspresso
Linux deps
If Chromium won’t start due to missing libraries, try
--with-deps (may require elevated privileges).
chmod +x cspresso.AppImage
./cspresso.AppImage https://example.com \
--browsers-path "$HOME/.cache/cspresso/pw-browsers"
Tip
AppImages mount read-only - use
--browsers-path to install browsers into a writable cache directory.
Verify releases with the mig5 GPG key (fingerprint
00AE817C24A10C2540461A9C1D7CDE0234DB458D).