Skip to main content

plain.scan

Scan websites for HTTP security misconfigurations.

Overview

Plain Scan checks production websites for common HTTP security issues: headers, SSL certificates, redirects, and server-level security settings.

You can try it immediately without installing anything:

uvx plain-scan github.com

Or visit plainframework.com/scan to scan URLs in your browser.

Plain Scan focuses on practical checks you should actually pay attention to. Every failure is highly actionable with specific guidance on what to fix.

Command line usage

Scan any URL by passing a domain or full URL:

plain-scan example.com
plain-scan https://example.com/login

Bare domains default to HTTPS.

Output formats

Choose between CLI, JSON, or Markdown output:

plain-scan example.com --format cli      # default, human-readable
plain-scan example.com --format json     # machine-readable
plain-scan example.com --format markdown # for reports

Disabling audits

Skip specific audits using --disable:

plain-scan staging.example.com --disable hsts --disable csp

This is useful for staging servers where you might not have HSTS configured yet.

Available audits to disable: csp, hsts, tls, redirects, content-type-options, frame-options, referrer-policy, cookies, cors.

Verbose mode

See the full response chain including headers and cookies:

plain-scan example.com --verbose

Using the Scanner programmatically

You can use the Scanner class directly in Python:

from plain.scan.scanner import Scanner

scanner = Scanner("https://example.com")
result = scanner.scan()

print(f"URL: {result.url}")
print(f"Passed: {result.passed}")
print(f"Audits: {result.passed_count}/{result.total_count} passed")

for audit in result.audits:
    status = "PASS" if audit.passed else "FAIL"
    print(f"  [{status}] {audit.name}")
    for check in audit.checks:
        print(f"    - {check.name}: {check.message}")

Disable specific audits by passing a set of slugs:

scanner = Scanner("https://staging.example.com", disabled_audits={"hsts", "csp"})

The scan result can be serialized to JSON:

import json

result_dict = result.to_dict()
print(json.dumps(result_dict, indent=2))

See ScanResult, AuditResult, and CheckResult for the full result structure.

Audits

Security checks are organized into audits. Each audit first checks if a security feature is detected, then runs specific checks to verify proper configuration.

Required audits

These audits fail if the security feature is missing or misconfigured:

Audit Description
CSP Content Security Policy protects against XSS and data injection
HSTS HTTP Strict Transport Security enforces HTTPS connections
TLS Validates SSL certificate and connection security
Redirects Ensures proper HTTP to HTTPS redirects
Content-Type-Options Prevents MIME-sniffing attacks
Frame-Options Prevents clickjacking attacks
Referrer-Policy Controls referrer information sharing

Optional audits

These audits only fail if the feature is detected but misconfigured:

Audit Description
CORS Cross-Origin Resource Sharing (only needed for cross-origin APIs)

Conditional audits

These audits are automatically detected and only checked when relevant:

Audit Description
Cookies Only checked if your site sets cookies in the response

FAQs

How does the scanner work?

Plain Scan makes a single unauthenticated GET request to the provided URL. It checks what can be inferred from the HTTP response and performs a TLS socket probe. It does not crawl additional pages, execute JavaScript, render in a browser, or follow authenticated flows.

Can I use this against development servers?

Yes. Plain Scan works against any URL, including localhost. Keep in mind that some checks (like TLS) may fail on development servers without valid certificates.

Why does the scanner flag Google Analytics or Google Tag Manager in my CSP?

These domains host JSONP endpoints that can be exploited to bypass CSP and execute arbitrary JavaScript. This is based on research from Google's CSP Evaluator team. If you must use these services, consider using nonce-based or hash-based CSP instead of domain allowlisting.

What about COOP/COEP/CORP headers?

Cross-origin isolation headers are not currently enforced. Most sites do not ship the full header trio yet, so Plain Scan treats them as optional for now.

What security standards does Plain Scan follow?

Plain Scan implements checks based on:

What tools complement Plain Scan?

Plain Scan focuses on HTTP-level security checks. For browser-based audits, performance, and client-side security, consider:

Installation

Web interface (no installation):

Visit plainframework.com/scan to scan any URL directly in your browser.

Command line (no installation):

uvx plain-scan github.com

This uses uvx to run plain-scan without adding it as a project dependency.

As a project dependency:

uv add plain.scan

Then run scans:

plain-scan example.com

Or use the Python API:

from plain.scan.scanner import Scanner

result = Scanner("https://example.com").scan()
print(f"Passed: {result.passed}")

Download files

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

Source Distribution

plain_scan-0.6.6.tar.gz (33.7 kB view details)

Uploaded Source

Built Distribution

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

plain_scan-0.6.6-py3-none-any.whl (41.7 kB view details)

Uploaded Python 3

File details

Details for the file plain_scan-0.6.6.tar.gz.

File metadata

  • Download URL: plain_scan-0.6.6.tar.gz
  • Upload date:
  • Size: 33.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_scan-0.6.6.tar.gz
Algorithm Hash digest
SHA256 144ea76435957ea8f8a25203c47d8b21dfaa31fcb75394a2df0b3e334da5dae0
MD5 7c55c200f21ac234bac461e1e25d3127
BLAKE2b-256 5e792452c48bb7831d32114ba17175e329646b61a8cf81bd745e4e8091a3dfb6

See more details on using hashes here.

File details

Details for the file plain_scan-0.6.6-py3-none-any.whl.

File metadata

  • Download URL: plain_scan-0.6.6-py3-none-any.whl
  • Upload date:
  • Size: 41.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_scan-0.6.6-py3-none-any.whl
Algorithm Hash digest
SHA256 01bd359bd1bdc1c501c25cd93c96175764c1d12a81ff0c7f6a21dedc3bf19367
MD5 5b94404ad0399edbffc56648b131a3bf
BLAKE2b-256 13713c46e3240e4b3962b79c95d5bdc4ea9a5f851f35796047108b0a7478afb9

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.6.6 This release

2 files

0.6.5

2 files

0.6.4

2 files

0.6.3

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

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