Skip to main content

PreviewShield

CI Python 3.10+ License: Apache-2.0 GitHub stars

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:

  1. Only HTTP(S) targets without URL credentials are accepted.
  2. Every hostname, including every redirect destination, is checked against the optional host allowlist, then resolved and validated.
  3. Non-public, loopback, link-local, reserved, multicast, and unspecified addresses are blocked by default.
  4. The socket connects to the exact validated address while HTTPS still uses normal hostname verification and SNI.
  5. 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

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

previewshield-1.0.0.tar.gz (204.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

previewshield-1.0.0-py3-none-any.whl (190.0 kB view details)

Uploaded Python 3

File details

Details for the file previewshield-1.0.0.tar.gz.

File metadata

  • Download URL: previewshield-1.0.0.tar.gz
  • Upload date:
  • Size: 204.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for previewshield-1.0.0.tar.gz
Algorithm Hash digest
SHA256 b842486b464cbfe18ac66d96488d7544ae39012a42d7b20158290a347f644bc4
MD5 952c6221ab5ad51580aa9a3baac391d4
BLAKE2b-256 22d89c9bdb2ea72f28cef65901044a5f3b210bf87f9f7223a049ed35a253207a

See more details on using hashes here.

Provenance

The following attestation bundles were made for previewshield-1.0.0.tar.gz:

Publisher: release.yml on devUmut35/PreviewShield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file previewshield-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: previewshield-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 190.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for previewshield-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 30ce2055fad1fe0677034b0631dea941728e6d2d73117929b4564576e308441e
MD5 afe43c802d7a0186a4bc249230483ccd
BLAKE2b-256 25187b7775cd1dacd2f95eb60d29d80d84e394cd69d2bddcfbe5efdb22e5948d

See more details on using hashes here.

Provenance

The following attestation bundles were made for previewshield-1.0.0-py3-none-any.whl:

Publisher: release.yml on devUmut35/PreviewShield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page