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/ --json

Requires 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/*' --json

Links 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.txt

Both files may be created even when a scan fails. Check the exit status and completeness before using either output.

FieldMeaning
schema_version2; update consumers of the earlier JSON format.
csp, directivesThe complete generated policy, including defaults, hashes and nonce templates. Earlier directives contained only observed external origins.
visited, pagesSuccessfully scanned final HTML URLs; per-page requested/final URL, redirects, HTTP status, completion and injection confirmation.
observationsEvidence 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_policyCandidate-policy violation records and the evaluated policy.
complete, errors, notesWhether the selected scan completed, failures and coverage qualifications.
header_bytesUTF-8 size including the header name and separator, excluding HTTP framing. The size budget is advisory.
sourcemapsOptional 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 --json

Candidate 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 codeMeaning
0Selected scan completed without observed candidate violations. It does not prove full application coverage.
1Selected scan completed with candidate violations.
2Invalid 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

OptionDefault / inputPurpose
--max-pages10Maximum attempted pages.
--timeout-ms20000Per-navigation timeout in milliseconds.
--settle-ms1500Extra wait after load/network settling; zero is allowed.
--scan-timeout120Overall scan seconds, excluding browser installation.
--max-requests2000Maximum intercepted HTTP requests.
--max-observations10000Maximum observation/violation records.
--header-budget8192Advisory header bytes.
--excludeRepeatableTop-level URL/path glob to skip.
--headedOffShow the browser.
--jsonOffStructured JSON schema v2.
--header-onlyOffPolicy value on stdout, diagnostics on stderr; cannot combine with --json.
--evaluateCSP stringInject a quoted candidate policy as Report-Only.
--evaluate-fileFile pathRead a UTF-8 policy file; cannot combine with --evaluate.
--bypass-cspOffStrip same-origin HTML CSP response headers, not meta CSP.
--include-sourcemapsOffInspect map metadata without granting connect-src permissions.
--ignore-non-htmlOffSuppress notes about skipped non-HTML documents; these are always excluded from hashing.
--browsers-pathUser cacheExplicit Chromium cache directory.
--no-installOffNever install missing browsers.
--with-depsOffInclude OS dependencies when installing a missing browser.
--no-sandboxOffExplicitly disable Chromium sandboxing for isolated, trusted workloads only.
--allow-blobOffInclude blob: in common directives.
--unsafe-evalOffInclude unsafe-eval in script-src.
--upgrade-insecure-requestsOffAdd 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.