securityscanner.org

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, then fix_first. That is the whole story for most readers.
  • Treat applicable: false as "no information", not as a pass.
  • When target.behind_cdn is set, open_ports describes the CDN's edge, not the customer's server.
  • requests.log is 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 disclaimer field 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.