Scanning: passive & active
The scanner is two passes with a strict boundary between them. Tutorial 3 walks the full ladder; this page is the reference.
Passive (free)
Reads only what's already captured and sends nothing, so it's safe at any point:
nullhawk scan passive ./engagement
nullhawk scan passive ./engagement --detector cache.sensitive # one check
nullhawk scan passive ./engagement --host api.example.com --since 2026-01-01T00:00:00Z
nullhawk scan passive ./engagement --no-save # print without recording
It produces observations (facts, filed as leads) and hypotheses (suspicions that wait for a verifier). Five hundred endpoints missing one header is one finding citing three exchanges, not five hundred findings. A run records which detectors ran, at which versions, including the ones that saw nothing — the row that turns silence into a fact.
Active (Pro)
Settles the hypotheses a passive pass raised by running experiments — the only scan pass
that sends traffic:
nullhawk scan active ./engagement --dry-run # show exactly what would be sent, to which hosts
nullhawk scan active ./engagement # run (asks to confirm)
nullhawk scan active ./engagement --yes # non-interactive (CI)
nullhawk scan active ./engagement --detector input.sqli --host app.example.com
nullhawk scan active ./engagement --quiet # drop the "loud" checks (OPSEC)
An active run only tests hypotheses a passive pass already raised — nothing is invented, so run
scan passive first. The scheduler sends one host's requests sequentially with a pause, runs
different hosts concurrently, and enforces a ceiling (--max-requests). A run that stops early
says so above its results.
The detector suite
nullhawk detectors # every check, its version, whether it sends, and its noise level
The suite spans reflected input, SQLi, command injection, SSTI, SSRF (incl. redirect→SSRF
chaining), path traversal, CRLF, open redirect, CORS, cache deception/poisoning, host-header
injection, HTTP request smuggling, access-control bypass, authentication enforcement,
scheduled cross-identity access, JWT guessable-secret, hidden parameters, DOM/stored XSS, and
more. Each detector carries an intrusiveness level (silent / moderate / loud); --quiet
drops the loud ones.
Custom checks
Define your own scan checks (Nullhawk's answer to Burp's BChecks):
nullhawk check ./engagement ... # add / list / enable user-defined checks
The zero-false-positive benchmark
nullhawk benchmark suite.json # score the scanner against labelled targets
Each case pairs a captured-traffic project with a labelled expectation (fire or clear).
The headline number is the false-positive count; any FP produces a non-zero exit, so it's
CI-gateable. It's the data behind the zero-FP claim.