Usage
Generate a draft policy, inspect its evidence and evaluate a candidate over selected coverage.
Install
# Released package
pipx install cspresso
# Or in a virtual environment
pip install cspresso
# From the source checkout, with Poetry >=2.2,<3
poetry sync --with dev
poetry run cspresso https://example.com/ --jsonRequires Python 3.10+ and Playwright’s Chromium. Source development uses Poetry 2.x (at least 2.2); CI pins 2.5.1. The lockfile retains the existing package versions. AppImages are available from Releases.
Run and control scope
cspresso https://example.com/docs/ --max-pages 10 \
--exclude '/logout*' --exclude '/delete/*' --jsonLinks resolve against the document’s actual URL and any <base href>. The scanner collects same-origin HTML, skips non-HTML documents, waits for load, allows a bounded network-idle wait, then applies the settle delay.
Start with the canonical HTTPS/www URL. Same-origin GET redirects are followed explicitly at the destination URL; top-level redirects outside the origin are rejected. Explicit redirect handling can change timing/history. Non-GET redirects are not replayed, and delayed redirects after initial load make the result incomplete.
--exclude matches top-level URL or path globs and is repeatable. It does not block subresources or act as a network firewall. Foreign frames can load, but their internal resources are not added to the parent’s policy. Same-origin and inherited-policy frames are included.
Output and JSON schema v2
Default text output includes comments and a proposed header line. --header-only prints just the policy value, with diagnostics on stderr; --json emits structured results. Installer messages stay on stderr.
cspresso https://example.com/ --json > scan.json
cspresso https://example.com/ --header-only > candidate-csp.txtBoth files may be created even when a scan fails. Check the exit status and completeness before using either output.
| Field | Meaning |
|---|---|
schema_version | 2; update consumers of the earlier JSON format. |
csp, directives | The complete generated policy, including defaults, hashes and nonce templates. Earlier directives contained only observed external origins. |
visited, pages | Successfully scanned final HTML URLs; per-page requested/final URL, redirects, HTTP status, completion and injection confirmation. |
observations | Evidence for permissions: document/frame, resource origin/URL or inline hash, directive and observation kind. An attempted request is not proof that a permission is necessary or a response succeeded. |
violations, evaluated_policy | Candidate-policy violation records and the evaluated policy. |
complete, errors, notes | Whether the selected scan completed, failures and coverage qualifications. |
header_bytes | UTF-8 size including the header name and separator, excluding HTTP framing. The size budget is advisory. |
sourcemaps | Optional developer metadata; map origins are not automatically granted connect-src access. |
URLs, query parameters and internal hostnames are not automatically redacted. Restrict report access and retention.
Inline scripts, styles and nonces
Hashes must match inline content exactly. For style="..." and event-handler attributes, hash authorisation requires 'unsafe-hashes'. Prefer external code or nonce-bearing elements where possible.
Script and style nonce requirements are tracked separately, including external script and stylesheet elements. Replace {NONCE} with a fresh unpredictable nonce for every HTML response, in both the header and matching tags. Actual observed nonce values are not emitted.
Data-only script blocks such as JSON-LD are skipped. Inline collection has count/text limits; truncation is reported as incomplete. Settled-DOM inspection can miss scripts removed earlier. Baseline permissions such as form-action and frame-ancestors still need manual review.
Evaluate a candidate
cspresso https://example.com/ --bypass-csp \
--evaluate-file candidate-csp.txt --jsonCandidate policies run in Report-Only mode. Existing enforcing headers or meta CSP that remain active make evaluation incomplete. --bypass-csp removes same-origin HTML response headers, not meta CSP. See the evaluation guide for attribution, failure handling and CI usage.
| Exit code | Meaning |
|---|---|
0 | Selected scan completed without observed candidate violations. It does not prove full application coverage. |
1 | Selected scan completed with candidate violations. |
2 | Invalid input, runtime failure or incomplete scan. This takes precedence when violations also exist. |
Browser sandbox and cache
Chromium’s process sandbox is enabled by default and does not prevent ordinary resource discovery. --bypass-csp changes document policy enforcement, not the process sandbox. Run as a non-root user with OS sandbox support. --no-sandbox is an explicit override for suitably isolated, trusted workloads; launch failures never enable it automatically.
Chromium is installed if its executable is missing. An existing browser that cannot launch is not repeatedly reinstalled. The default cache is under the user cache directory, for example ~/.cache/cspresso/pw-browsers on Linux. Override it with --browsers-path or PLAYWRIGHT_BROWSERS_PATH; explicit paths also work with --no-install. The environment variable’s special value 0 retains Playwright’s package-local convention.
Use an owned, non-shared cache directory. Unsafe/symlink paths are rejected, there is no shared temporary fallback, and installation locks are released by the OS when a process exits. AppImages use the same writable user-cache default.
# From a source checkout, if OS libraries are missing:
poetry run playwright install-deps chromium--with-deps requests OS dependencies when a browser installation is needed; it may need elevated privileges. See Security for the threat model.
For source tests, ./tests.sh uses a private temporary browser directory and removes it on exit. An explicit PLAYWRIGHT_BROWSERS_PATH is validated before installation and preserved. This avoids permissive runner cache directories causing pre-launch failures.
Bounds and coverage
A page limit defines finite coverage and is recorded in notes. Request/observation truncation or the overall deadline makes the result incomplete. These are not hard browser memory, network-byte or disk quotas; use external resource limits for untrusted content.
Service workers are blocked for deterministic interception. Worker-dependent applications require separate testing. Worker-internal requests without document provenance, CSS-embedded data URLs, shadow DOM, removed early scripts, authenticated interactions and other browsers are not exhaustively covered. WebSockets are observed explicitly when frame attribution is unambiguous; mixed-origin ambiguity is reported as incomplete.
--include-sourcemaps now inspects metadata only. Unknown, large or compressed bodies may be skipped; headers can still be inspected. A map pointer is not evidence that the application needs a new connection permission.
Flag reference
| Option | Default / input | Purpose |
|---|---|---|
--max-pages | 10 | Maximum attempted pages. |
--timeout-ms | 20000 | Per-navigation timeout in milliseconds. |
--settle-ms | 1500 | Extra wait after load/network settling; zero is allowed. |
--scan-timeout | 120 | Overall scan seconds, excluding browser installation. |
--max-requests | 2000 | Maximum intercepted HTTP requests. |
--max-observations | 10000 | Maximum observation/violation records. |
--header-budget | 8192 | Advisory header bytes. |
--exclude | Repeatable | Top-level URL/path glob to skip. |
--headed | Off | Show the browser. |
--json | Off | Structured JSON schema v2. |
--header-only | Off | Policy value on stdout, diagnostics on stderr; cannot combine with --json. |
--evaluate | CSP string | Inject a quoted candidate policy as Report-Only. |
--evaluate-file | File path | Read a UTF-8 policy file; cannot combine with --evaluate. |
--bypass-csp | Off | Strip same-origin HTML CSP response headers, not meta CSP. |
--include-sourcemaps | Off | Inspect map metadata without granting connect-src permissions. |
--ignore-non-html | Off | Suppress notes about skipped non-HTML documents; these are always excluded from hashing. |
--browsers-path | User cache | Explicit Chromium cache directory. |
--no-install | Off | Never install missing browsers. |
--with-deps | Off | Include OS dependencies when installing a missing browser. |
--no-sandbox | Off | Explicitly disable Chromium sandboxing for isolated, trusted workloads only. |
--allow-blob | Off | Include blob: in common directives. |
--unsafe-eval | Off | Include unsafe-eval in script-src. |
--upgrade-insecure-requests | Off | Add the upgrade-insecure-requests directive. |
Run cspresso --help for the installed CLI. Page, timeout, request, observation and header budgets must be positive; candidate policies must be nonempty.