ProofScan

Scanners guess. ProofScan proves.

A web vulnerability scanner that tests every suspicious finding before it reports it, and keeps the evidence.

Replay a real scan Sample PDF report Source on GitHub
ProofScan replaying a scan of DVWA: 12 suspicious, 6 proved real, 6 proved false and thrown out

The problem

Security scanners report a lot of things that are not real. Someone then has to check every alert by hand, which is the slow part of a security test. ProofScan does that checking itself. It looks for SQL injection and cross-site scripting (XSS), and every candidate gets a second, active test that tries to prove it. Only what it can prove goes in the report.

How it proves things

True/false test

Send an always-true and an always-false condition that differ in nothing else. If the pages differ by more than the page differs from itself, the database ran our input.

Timing test

For pages that never change. Two inputs, a sleep behind a true or a false condition. Confirmed only if the true side is slower every single time.

Real browser

Load the page in headless Chromium. The script has to hand back a random token only it knows. Coming back in the page is not enough, it has to run.

Results against OWASP ZAP

On DVWA, a deliberately vulnerable app I did not write, scored on SQL injection and XSS against a hand-written answer key:

AlertsCorrectFalse alarmsPrecisionRecall
ProofScan, verification off12660.500.86
ProofScan, verification on6601.000.86
OWASP ZAP 2.177610.860.86

The same numbers came out of four ProofScan scans and two ZAP scans, six weeks apart. ZAP also checks around a hundred kinds of problem that ProofScan does not, so this is a trade of coverage for certainty, not a claim that ProofScan beats ZAP. Everyone missed DOM-based XSS.

Verify mode: checking ZAP's own findings

ProofScan can also take ZAP's report and prove or reject each finding in it. ZAP keeps its wide coverage, and ProofScan adds certainty to the part it can test. ZAP's report only names the page and the input, so ProofScan rebuilds each request by reading the form on that page, then runs the same tests.

AlertsCorrectFalse alarmsPrecisionRecall
ZAP alerts, as reported7610.860.86
ZAP alerts, verified by ProofScan6601.000.86

All 6 real findings were proved, and the one removed is the CSP page below. It took 78 requests, against 368 for a full ProofScan scan, because ZAP had already done the searching.

Replay the verified ZAP scan

The case that shows the idea

On DVWA's CSP page our input comes back in the page completely unescaped. ZAP reports it as XSS, High risk. But the page sends a Content-Security-Policy header, so the browser refuses to run the script. ProofScan opened it in a real browser, five payloads ran zero times, and it threw the finding out. Reading the response cannot tell "came back" from "ran". Running it can.

ProofScan's card for DVWA's CSP page: thrown out, none of the 5 payloads ran
Thrown out: the page looks normal because the script never ran.

Safe by design

Built with

Python, Playwright (headless Chromium), Flask, SQLite and Docker. CVSS v3.1 scoring with CWE and OWASP Top 10 mapping, PDF reports, and 194 automated tests.