The scan result spec
One line: every scan produces one JSON object, the same data as the emailed report, and it follows the JSON Schema at /spec/v1.json, so a script or an AI agent can read a result without guessing.
Where to get it
The schema is at https://securityscanner.org/spec/v1.json (JSON Schema, draft 2020-12). A scan result is scan.json. For a signed-up domain it is linked from the report email. The current schema_version is 0.1. Fields may be added at any time without a version bump; a field only changes meaning or disappears with one.
The shape in one picture
- scanner
- who produced it: name, version, the exact User-Agent, mode (
light) - domain
- the apex domain scanned, lower case
- scanned_at
- ISO 8601 start time, UTC
- target
- what was reached: IPs, final home page URL and status, whether it was https, which CDN fronts it (if any)
- overall
- the overall grade and score, the raw weighted score, and which cap applied and why
- fix_first
- up to five findings to act on first, in order
- checks
- the eight checks, always in the same order, each with grade, score, summary, findings and evidence
- requests
- what the scanner sent: request counts, DNS queries, port probes, and the full request log
- errors
- checks that could not run (empty when all ran)
- disclaimer
- the outside-of-the-building statement, ending "No audit has been done."
The eight checks
checks is always these eight ids in this order, with these weights: security_headers 15, tls 20, email_auth 15, dns_hygiene 5, exposed_files 20, wordpress 10, page_hygiene 5, open_ports 10. Each has applicable (false when it does not apply, for example a site that is not WordPress, or could not run), a score 0 to 100, a grade, a one-line summary, a list of findings and a check-specific evidence object whose shape is written out in the schema.
Findings and severities
A finding is {severity, title, detail, fix}. Severity is one of critical, high, medium, low, info, good. The last two never cost points; they are there so the positive results are visible. title is short and stable, so a script can match on it. detail says what was seen and why it matters. fix says what to do, in plain words.
How the overall grade is derived
Letters: A 90 to 100, B 80 to 89, C 70 to 79, D 60 to 69, F under 60. The overall score starts as the weighted average of the applicable checks (overall.weighted_score, weights in overall.weights_used). It is then capped at one letter above the worst grade among the four heavy checks (weight 15 or more), and capped at D when any check has a critical finding. overall.cap and overall.cap_reason say which rule applied, or are null when none did. The rule is deliberate: an attacker needs one open door, so the big four should move the grade and not be diluted by averages.
For AI agents reading a result
- Start at
overall.grade, thenfix_first. That is the whole story for most readers. - Treat
applicable: falseas "no information", not as a pass. - When
target.behind_cdnis set,open_portsdescribes the CDN's edge, not the customer's server. requests.logis every HTTP request the scanner made. It is there so anyone can see exactly what touched the site.- The scan is outside-only and read-only. It is not a penetration test, it is not part of SOC 2, and a clean result is not a guarantee. The
disclaimerfield says so in every file.
Validating
Any JSON Schema 2020-12 validator works. In Python, with the jsonschema package: Draft202012Validator(schema).validate(scan). The schema allows extra fields everywhere so that new fields do not break old readers.
Questions
abuse@securityscanner.org. Security issues in the scanner itself: security@securityscanner.org, also in /.well-known/security.txt.