PreviewShield
Stop a pull request from quietly weakening your web security.
PreviewShield is a policy-as-code scanner and CI regression gate for web deployments. It checks security headers, cookies, CORS, redirects, TLS, and response health, then compares a pull-request preview with production. Existing production debt stays visible, while the gate can fail only on findings that are new or more severe.
PreviewShield diff: FAIL
Baseline: https://example.com (A, 91/100)
Preview: https://preview.example.dev (B, 83/100)
Failure threshold: high
Changes: 1 regressions, 0 resolved, 0 changed, 18 unchanged
REGRESSION (1):
[HIGH] PS1101 - Enforced Content-Security-Policy is missing
Why PreviewShield?
- Purpose-built preview diffs. Match findings by rule, route, and subject across different hostnames, so production and ephemeral deployments compare cleanly.
- Policy that lives with the code. Choose a profile, scan multiple routes, override severities, disable accepted rules, and require organization-specific headers in YAML.
- CI-native outputs. Render console, JSON, Markdown, SARIF 2.1.0, JUnit XML, or a standalone HTML report from the same scan.
- A local release control room. Open a no-account browser interface for guided scans, deployment comparisons, finding filters, and report downloads.
- Safe network defaults. Block non-public addresses, validate every redirect, pin connections to validated DNS answers, preserve TLS hostname verification, and avoid environment proxies.
- Actionable checks. Every finding has a stable rule ID, severity, evidence, remediation, and reference link.
- Small and portable. Python 3.10+ with one runtime dependency, PyYAML. Response bodies are not downloaded.
flowchart LR
P["Production"] --> S1["Scan selected routes"]
V["PR preview"] --> S2["Scan selected routes"]
Y[".previewshield.yml"] --> S1
Y --> S2
S1 --> D["Match rule + route + subject"]
S2 --> D
D --> G{"New or severity increased at threshold?"}
Y --> G
G --> R["Console / JSON / Markdown / SARIF / JUnit / HTML"]
Quick start
Install a released version from PyPI:
python -m pip install previewshield
Until the first PyPI release, install directly from the repository:
python -m pip install "git+https://github.com/devUmut35/PreviewShield.git"
Prefer a browser? Launch the local-only interface:
previewshield ui
PreviewShield opens a guided release control room on 127.0.0.1. It supports single-site scans,
production-to-preview comparisons, result filtering, and HTML, JSON, Markdown, SARIF, or JUnit
downloads without an account or hosted scanning service. See the web UI guide.
Scan one deployment:
previewshield scan https://example.com
Compare production with a pull-request preview and fail on high or critical regressions:
previewshield diff \
--baseline https://example.com \
--preview https://pr-142.example.dev \
--fail-on high
PreviewShield returns 0 when the policy passes and 1 when the configured threshold is
crossed, making the command a drop-in CI gate.
Add it to a pull request
Run this job after your preview deployment. Replace vars.PREVIEW_URL with the URL produced by
your deployment provider.
name: Preview security
on:
pull_request:
permissions:
contents: read
jobs:
previewshield:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Compare preview with production
id: previewshield
uses: devUmut35/PreviewShield@v1
with:
baseline: https://example.com
preview: ${{ vars.PREVIEW_URL }}
config: .previewshield.yml
paths: |
/
/login
/api/health
fail-on: high
format: sarif
output: previewshield.sarif
The Action writes a job summary, generates JSON, Markdown, and SARIF sidecars, and exposes
report, score, grade, and passed outputs. See the
GitHub Action guide for artifact and code-scanning examples.
Put the policy in your repository
Generate and validate a starter policy:
previewshield init
previewshield policy validate .previewshield.yml
version: 1
name: public-web
profile: balanced
fail_on: high
paths:
- /
- /login
- /api/health
network:
timeout_seconds: 10
max_redirects: 5
allow_private: false
allowed_hosts:
- example.com
- "*.example.dev"
checks:
min_hsts_max_age: 15552000
certificate_warning_days: 30
disabled: []
severity_overrides:
PS1204: medium
required_headers:
X-Robots-Tag:
severity: medium
contains: noindex
diff:
mode: regressions
Unknown keys and invalid values are rejected instead of being silently ignored. Read the
policy reference for every option and the difference between
regressions and absolute modes.
Useful commands
# Scan several routes
previewshield scan example.com --path / --path /login --path /api/health
# Produce a human report and CI sidecars in one request
previewshield scan example.com \
--format html --output report.html \
--also-format sarif=report.sarif \
--also-format junit=report.xml
# Use a request header for an authenticated preview; values are not written to reports
previewshield scan preview.example.dev \
--header "Authorization: Bearer $PREVIEW_TOKEN"
# Inspect stable rule metadata
previewshield rules
previewshield rules --json
# Open the local browser interface without launching a new browser tab
previewshield ui --no-open
The command surface also includes diff, init, and policy validate. Run
previewshield COMMAND --help for all options.
What it checks
PreviewShield currently ships 30 stable rules across:
| Area | Examples |
|---|---|
| Transport and TLS | HTTPS, redirect downgrade, negotiated TLS, cipher, certificate expiry |
| Browser hardening | HSTS, CSP, clickjacking, MIME sniffing, referrer and permissions policies |
| Cross-origin policy | Wildcard or opaque origins, credentialed CORS, missing Vary: Origin |
| Cookies | Secure, HttpOnly, SameSite, __Host- and __Secure- prefix contracts |
| Response health | Client/server errors and exposed technology headers |
| Project policy | Required response headers and project-specific severity decisions |
See the rule catalog for IDs, default severities, and remediation intent.
Reports and automation
| Format | Best for |
|---|---|
console |
Local terminal feedback |
json |
Automation and long-term storage |
markdown |
Job summaries and pull-request comments |
sarif |
GitHub code scanning and SARIF-compatible platforms |
junit |
CI test-report viewers |
html |
Shareable, standalone human reports |
Output is consistently structured, sorted, and sanitized: credentials, query strings, fragments, cookie-like fields, and common token patterns are redacted. Details are in the output guide.
Security model
Scanning URLs from CI creates an SSRF boundary. PreviewShield treats it as one:
- Only HTTP(S) targets without URL credentials are accepted.
- Every hostname, including every redirect destination, is checked against the optional host allowlist, then resolved and validated.
- Non-public, loopback, link-local, reserved, multicast, and unspecified addresses are blocked by default.
- The socket connects to the exact validated address while HTTPS still uses normal hostname verification and SNI.
- All caller-supplied headers are removed on cross-origin redirects, environment proxies are ignored, and response bodies are never read.
The optional browser UI binds only to 127.0.0.1 and requires an exact Host, same-origin POST,
HttpOnly session cookie, and CSRF token. Private targets remain locked unless the user starts that
session with previewshield ui --allow-private-targets and confirms authorization in the UI.
--allow-private deliberately relaxes the network boundary and should be used only for trusted
local test targets. PreviewShield is a hardening auditor, not a vulnerability scanner or proof
that a site is secure. Read the complete security model and only scan
systems you own or are authorized to test.
Documentation
- Getting started
- Local web interface
- Policy reference
- GitHub Action
- Output formats
- Rule catalog
- Security model
- Python API
- Migrating from Security Header Auditor
Contributing
PreviewShield is Apache-2.0 licensed and built in the open. Bug reports, rule proposals, reporter integrations, tests, documentation, and security review are welcome. Start with CONTRIBUTING.md, browse good first issues, or open a focused feature request.
If PreviewShield protects one of your releases, consider starring the repository. It helps other teams discover a practical security regression gate.
Security vulnerabilities should be reported privately according to SECURITY.md. General support expectations are documented in SUPPORT.md.
License
Copyright 2026 Umutcan Altan. Licensed under the Apache License 2.0.
Metadata
Release files for previewshield 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| previewshield-1.0.0.tar.gz | 204.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| previewshield-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 394.4 kB
Release files / previewshield-1.0.0.tar.gz
| Download URL | previewshield-1.0.0.tar.gz |
|---|---|
| Size | 204.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b842486b464cbfe18ac66d96488d7544ae39012a42d7b20158290a347f644bc4
|
|
BLAKE2b-256 checksum How to use checksums |
22d89c9bdb2ea72f28cef65901044a5f3b210bf87f9f7223a049ed35a253207a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 22, 2026.
Transparency logRelease files / previewshield-1.0.0-py3-none-any.whl
| Download URL | previewshield-1.0.0-py3-none-any.whl |
|---|---|
| Size | 190.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
30ce2055fad1fe0677034b0631dea941728e6d2d73117929b4564576e308441e
|
|
BLAKE2b-256 checksum How to use checksums |
25187b7775cd1dacd2f95eb60d29d80d84e394cd69d2bddcfbe5efdb22e5948d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 22, 2026.
Transparency log