Skip to content

Blog

sec-headers: Audit Your Security Headers in One Command

Security headers are the cheapest defense you can deploy. They cost nothing, take minutes to configure, and block entire classes of attacks — XSS, clickjacking, MIME sniffing, SSL stripping. Yet most sites get them wrong, or don't set them at all.

I built sec-headers, a fast Python CLI that audits seven critical HTTP security headers on any website and gives you a straight A+ through F grade. Here's how it works and why you should run it on your own stuff.


The seven headers that matter

Not all security headers are equal. I weighted mine by real-world impact:

Header Weight What it does
Strict-Transport-Security ★★★★★ Enforces HTTPS; kills SSL stripping
Content-Security-Policy ★★★★★ Your primary XSS and injection defense
X-Content-Type-Options ★★★★ Blocks MIME-type sniffing attacks
X-Frame-Options ★★★★ Clickjacking protection
Referrer-Policy ★★★ Controls how much info leaks via referrer
Permissions-Policy ★★★ Restricts browser feature/API access
X-XSS-Protection ★★ Legacy XSS filter (CSP supersedes it)

So Content-Security-Policy and Strict-Transport-Security each carry 5x the weight of X-XSS-Protection in the final grade. Missing HSTS hurts far more than a weak referrer policy — and it should.


Why I built this

I wanted a single command that answers the question: "Are these headers actually doing anything?"

Existing tools either require a browser extension (manual, doesn't scale), are SaaS with rate limits, or give you a pass/fail without telling you why.

sec-headers is different. It's a CLI-first, pipe-friendly tool that:

  • Works on any URL — give it one or a thousand
  • Explains every finding — not just "fail," but what's wrong and how to fix it
  • Outputs JSON — wire it into CI, scripts, or bug bounty reports
  • Scores honestly — weighted grades that prioritize critical headers

What it looks like

$ sec-headers https://example.com

━━━ https://example.com ━━━
  Status: 200  │  Grade: B (82/100)  │  Time: 234ms
  ✓ 4 pass  ⚠ 2 warn  ✗ 1 fail

  Header                         Status   Detail
  ──────────────────────────────  ──────── ──────────────────────────────────────
  Strict-Transport-Security      ✓ PASS   max-age=31536000, includeSubDomains
  Content-Security-Policy        ⚠ WARN   contains 'unsafe-inline'
  X-Content-Type-Options         ✓ PASS   'nosniff'
  X-Frame-Options                ✓ PASS   DENY
  Referrer-Policy                ✗ FAIL   missing
  Permissions-Policy             ⚠ WARN   missing
  X-XSS-Protection               ✓ PASS   '1; mode=block'

Every header gets a clear message: what's right, what's wrong, and what to do about it. No guesswork.


Install and run

pip install git+https://github.com/barry-oc/sec-headers.git

# Single URL
sec-headers https://yoursite.com

# Pipe from other tools
subfinder -d example.com | httpx -silent | sec-headers

# Batch audit with JSON output
sec-headers -f targets.txt --json | jq '.[] | {url, grade}'

The most common problems

Running this against a handful of sites, patterns emerge fast:

  1. Missing Referrer-Policy — by far the most common failure. Set it to strict-origin-when-cross-origin and move on.
  2. CSP with unsafe-inline — CSP exists but is toothless. If you're loading third-party scripts with inline handlers, you haven't really adopted CSP.
  3. Permissions-Policy absence — almost nobody sets this.
  4. HSTS without includeSubDomains — max-age looks good, but subdomains are left unprotected.

Each of these is a 5-minute fix. The hard part is knowing they exist.


Wire it into CI

The tool exits with code 1 if any header fails, so it slots right into a pipeline:

# GitHub Actions
- name: Audit security headers
  run: |
    pip install git+https://github.com/barry-oc/sec-headers.git
    sec-headers https://staging.example.com

Bust the build if your staging environment drops HSTS during a deploy.


What's next

I'm shipping one tool a week. On the shortlist:

  • --diff mode — compare headers before and after a config change
  • Additional header checks (Clear-Site-Data, Cross-Origin-*)
  • Docker image for ephemeral CI use

Run it on your own site. github.com/barry-oc/sec-headers

Pull requests and bug reports welcome.

vt-scanner: Check Any File Against VirusTotal in One Command

If you handle suspicious files — malware samples, email attachments, downloads from sketchy URLs — you've done the dance: hash the file, open VirusTotal, paste the hash, wait, repeat. It's slow, manual, and burns your attention on process instead of analysis.

I built vt-scanner, a CLI that automates the entire workflow. Point it at a directory and it hashes every file, checks VirusTotal, caches results, and tells you what's clean and what's not. No browser tabs, no copy-paste, no wasted API calls.


What it does

Three subcommands, each for a different workflow:

scan — one-shot audit

vt-scanner scan ./malware-samples

Hashes every file, checks VT, prints a verdict table:

FILE                                 VERDICT    ENGINES  NAME                LAST SEEN
──────────────────────────────────── ────────── ──────── ─────────────────── ────
samples/legit-setup.exe              CLEAN       0/72   setup_installer      2026-10-15
samples/backdoor.dll                 MALICIOUS  12/72   Trojan.Generic       2026-10-18
samples/unknown.bin                  UNKNOWN       -    -                         -

Malicious files are highlighted in red, suspicious in yellow, clean in green. JSON output is available for scripting.

monitor — continuous watch

vt-scanner monitor ~/Downloads --interval 60

Monitors a directory forever. New or modified files get hashed and checked automatically. Good for a malware analysis VM, a shared download folder, or monitoring where you drop samples during triage.

cache — don't waste API calls

vt-scanner cache stats

Every lookup is stored in a local SQLite database. If a file hasn't changed (same path, same modification time), the cached result is reused — zero API hits. This is the main upgrade over the manual copy-paste workflow.


Install and run

pip install git+https://github.com/barry-oc/vt-scanner.git
export VT_API_KEY="your_key_here"

vt-scanner scan ./samples
vt-scanner monitor ./incoming --interval 30

VirusTotal free tier allows 4 requests per minute. The scanner spaces requests automatically — set --rate-limit if you have a higher-tier key.


Why I built this

The original version of this tool was a loop-and-sleep Python script I'd been using for months. It worked but had all the usual raw-script problems:

  • API key hardcoded in the file
  • time.sleep(30) between every request regardless of rate limit
  • Results printed as unformatted Python objects
  • No caching — same file checked 50 times
  • Only SHA-256

The rewrite adds proper CLI design (argparse, three subcommands), structured caching (SQLite with path+mtime tracking), formatted output (color tables + JSON), and support for SHA-256, SHA-1, and MD5. Same core idea, but it doesn't feel like a script someone duct-taped together at 2am.


Pipe it

JSON output makes it scriptable:

# Find all malicious files
vt-scanner scan ./samples --json | jq '.[] | select(.result.last_analysis_stats.malicious > 0)'

# Export clean files to a list
vt-scanner scan ./samples --json | jq -r '.[] | select(.result.last_analysis_stats.malicious == 0) | .path' > clean.txt

Run it on your samples. github.com/barry-oc/vt-scanner

Pull requests and feature ideas welcome — especially if you have an edge case in your malware workflow I haven't thought of yet.