Reference

Check coverage

All 127 checks, by module, with the severity each finding carries. There are no tiers. Every scan runs every check — what varies is how much of your application the scanner is allowed to reach.

127
distinct checks
25
engine modules
138
signatures behind them
Before the check list

What changes coverage

Two scans can return very different amounts of detail, and it is never because one bought a better plan. Four things decide how deep a scan reaches.

Domain verification

All active probing

A DNS TXT record at _secscan-challenge.<domain>, or a file at /.well-known/secscan-verification.txt

Without it only passive checks run. This is the control that stops the platform being used as an anonymous attack proxy, so it fails closed — a lookup error denies rather than allows.

Credentials

The surface behind the login

A session cookie or bearer token you paste, or a form login driven by a headless browser

On most applications nearly everything of value sits behind auth. A scan that never signs in never sees it.

A second account

Broken access control (OWASP A01)

A second set of credentials, supplied alongside the first

The question is not whether a page is reachable but whether the wrong person can reach it. Comparing one account against nothing proves nothing.

An out-of-band collector

SSRF detection (OWASP A10)

Operator-side configuration (OOB_BASE_URL) — set on secscan.us

A server tricked into fetching an attacker URL produces nothing in its own response. Proof has to arrive somewhere else.

Unverified domains get passive checks only. Active probing stays locked until you prove you control the target, and the report says so explicitly rather than quietly returning less. See how verification works.

28 checks · scanner.ts

Response headers and cookies Passive

Passive analysis of the first response. The largest single module — CSP alone accounts for seven distinct findings, and cookies are graded differently depending on whether they carry a session.

Check Severity
No HTTPS / plaintext HTTP Critical
Missing Content-Security-Policy High
CSP script-src contains a wildcard High
CSP is report-only, not enforced Medium
Weak CSP — unsafe-inline or unsafe-eval in script-src Medium
CSP allows inline styles Low
CSP missing object-src 'none' Low
CSP missing base-uri directive Low
Missing HSTS Medium
HSTS max-age too short under 6 months Low
Missing clickjacking protection (X-Frame-Options) Medium
Mixed content on an HTTPS page Medium
Permissive CORS — wildcard origin Medium
Missing X-Content-Type-Options: nosniff Low
Missing Referrer-Policy Low
Missing Permissions-Policy Low
Missing COOP / COEP / CORP three separate findings Info
Missing Cache-Control headers Info
Server version disclosure Info
X-Powered-By discloses the stack Info
Session cookie missing Secure High
Session cookie missing HttpOnly Medium
Session cookie missing SameSite Medium
Non-session cookie missing Secure Low
Non-session cookie readable by JavaScript Info
Non-session cookie missing SameSite Info
17 checks · probes.ts

Active HTTP probes

Individual requests to specific paths, methods and origins. Every path hit is confirmed against the target’s own catch-all response before it is trusted.

Check Severity
Supabase tables exposed — RLS disabled Critical
Flask debugger console exposed Critical
Sensitive file or path publicly readable 78 paths — .env, .git, actuators, admin panels, WEB-INF, backups Varies
CORS — credentials allowed from an arbitrary origin High
CORS — origin reflected without an allowlist High
Dangerous HTTP methods advertised PUT, DELETE, PATCH High
HTTP traffic not redirected to HTTPS High
Directory listing enabled Medium
Open redirect Medium
HTTP TRACE enabled (cross-site tracing) Medium
HTTP CONNECT enabled Medium
External resources missing Subresource Integrity Medium
X-Frame-Options misconfigured Medium
robots.txt discloses sensitive paths Low
No rate limiting detected Low
Missing security.txt (RFC 9116) Info
Supabase detected — verify RLS Info
9 checks · dnsChecks.ts

DNS and email authentication Passive

Resolved over Cloudflare DNS-over-HTTPS. Needs no access to your registrar.

Check Severity
SPF uses +all — any sender authorised Critical
SPF record missing Medium if the domain has no MX High
DMARC record missing High
SPF uses ?all (neutral) Medium
DMARC policy is p=none Medium
SPF exceeds the 10 DNS lookup limit Low
DKIM records missing probes 30 selectors Low
DMARC missing rua= report address Info
DNSSEC not enabled Info
3 checks · mailTls.ts

Mail transport security Passive

SPF and DMARC say who may send mail as your domain. They say nothing about whether that mail is encrypted in transit, so a site can score perfectly on HTTPS while its password-reset mail is relayed in cleartext. This opens a real SMTP conversation with each MX on port 25 — the port every message from the outside world arrives on.

Check Severity
MX TLS certificate expired High
MX does not advertise STARTTLS Medium
MX TLS certificate expires within 21 days Low
9 checks · nextjsProbe.ts

Next.js __NEXT_DATA__ exposure Passive

Next.js serialises server props into a JSON blob in the page. Anything a getServerSideProps handler forgot to strip is sitting in the HTML, readable without running any JavaScript.

Check Severity
Supabase service_role key in __NEXT_DATA__ Critical
AWS access key ID / secret access key two findings Critical
Stripe live secret key Critical
GitHub token Critical
Private key Critical
Hardcoded database connection string Critical
SendGrid API key High
Slack token High
9 checks · structuredData.ts

Structured data and social metadata Passive

Structured data is generated by a CMS or a build step and routinely carries more than its author realised into a public document. The first three findings are genuine exposure and carry weight; the rest are presentation quality and are Info at weight zero — a site with no og:image must never grade as less secure than one with it, or the grade stops meaning security.

Check Severity
Internal hostname exposed in JSON-LD Low
Social preview asset loaded over http:// from an HTTPS page Low
Canonical URL points to a domain you do not own a classic SEO-injection symptom Low
Malformed JSON-LD Info
JSON-LD missing @context or @type Info
No structured data found Info
Incomplete Open Graph metadata Info
Twitter Card metadata absent Info
Page excluded from search indexes Info
7 checks · apiProbe.ts

API surface testing

Endpoints are recovered from an exposed OpenAPI document where one exists, otherwise from string literals in the JS bundle — a single-page app exposes no forms or query strings to discover.

Check Severity
SQL injection in a query, path or JSON body parameter Critical
Record endpoint serves another account’s data (IDOR) Critical
Write verb answers an unauthenticated caller with 404 (BFLA) Critical
Endpoint returns data with no session at all High
Response carries hashes, tokens, keys or card data High
Endpoint never throttles a burst Medium
Request body declares privilege-bearing fields Medium
7 checks · cveCheck.ts

Known CVEs and end-of-life software

Detected versions are queried against OSV.dev; severity follows the CVE’s own CVSS score. End-of-life checks are separate — unsupported software has no CVE for the patches that will never be written.

Check Severity
Detected version matched against OSV.dev Varies
IIS 6.0 — CVE-2017-7269 WebDAV RCE Critical
PHP end-of-life (5.x, 7.0–7.3) Critical
Apache end-of-life (1.x, 2.2) Critical
Nginx end-of-life High
7 checks · supabase-probes.ts

Supabase

The anon key is extracted from the bundle, PostgREST is asked for its schema, and up to 12 tables are tested with real unauthenticated calls. The anon key itself is never flagged — it is public by design.

Check Severity
service_role key in client-side JavaScript bypasses every RLS policy Critical
Tables return rows unauthenticated — CVE-2025-48757 Critical
Tables accept unauthenticated INSERT Critical
Tables reachable but currently empty no policy, just no data yet High
Storage bucket list exposed to the anon key Medium
Supabase detected, RLS appears configured Info
6 checks · jwtAnalysis.ts

JWT analysis Passive

Tokens are read from headers and HTML already fetched. No extra requests.

Check Severity
alg:none — signature verification bypass Critical
Empty signature segment Critical
Missing exp claim — token never expires High
HS256 without expiry High
Excessive lifetime (over 365 days) Medium
Sensitive data in the payload Medium
3 checks · accessControlProbe.ts

Broken access control

OWASP A01, the highest-ranked risk category and the one automated scanners usually skip. The same URL is requested as the primary account, as a second account, and as nobody, then the three responses are compared.

Check Severity
Second account served the first account’s record Critical
Records reachable by altering the identifier Critical
Record served in full with no session High
3 checks · firebase-probes.ts

Firebase

Twenty common collection names are tried against the Firestore REST API. The Firebase API key is not flagged — it identifies the project and is public by design.

Check Severity
Firestore returns documents unauthenticated Critical
Realtime Database returns data unauthenticated Critical
Firebase detected, rules appear restrictive Info
3 checks · storageProbe.ts

Object storage exposure

Bucket references are extracted from HTML and JS, then listed read-only.

Check Severity
Public S3 bucket listing High
Public Google Cloud Storage bucket listing High
Public Azure Blob container listing High
2 checks · baasProbes.ts

PocketBase and Appwrite

The same open-backend problem on two less common platforms, detected from page HTML and inline JS and then tested live.

Check Severity
PocketBase collections readable unauthenticated Critical
Appwrite collections readable unauthenticated Critical
2 checks · graphqlProbe.ts

GraphQL

Confirmed by a real introspection response rather than by the word "graphql" appearing on a page.

Check Severity
Schema introspection enabled in production Medium
GraphQL endpoint exposed Info
2 checks · injectionProbe.ts

Injection probing

Candidate parameters are collected from the URL, in-page forms and links, capped at 20. Detection only — nothing is exploited and no data is extracted.

Check Severity
SQL injection (error-based) a SQL error signature absent from the baseline Critical
Reflected cross-site scripting a random canary must reflect back unencoded High
1 check · worker.ts

TLS

Delegated to Qualys SSL Labs, started in parallel with the first request and waited on for up to 120 seconds. Certificate expiry from the same result feeds the monitoring alerts.

Check Severity
SSL Labs grade C or below F is Critical, C is Medium Critical
1 check · recon.ts

Recon — ports, DNS and subdomains

A TCP-connect sweep over 30 common ports with a banner grab, alongside DNS record enumeration and subdomain discovery from crt.sh certificate transparency logs plus a wordlist, capped at 100 resolved results.

Check Severity
Unexpected open port severity per port Varies
2 checks · crawler.ts

Site crawl

Up to 20 internal pages, with the header and cookie checks re-run on each and 8 high-value paths probed per page. A site is rarely uniform — the login page and the marketing home page often ship different headers.

Check Severity
Header or cookie failure on an inner page Varies
Sensitive path found on a crawled page 8 paths per page Varies
1 check · subdomainTakeover.ts

Subdomain takeover

CNAME chains are followed and matched against 19 provider fingerprints — S3, Heroku, GitHub Pages, Netlify, Vercel, Azure, Fastly, Shopify, Fly.io, Render, Railway and more.

Check Severity
Dangling CNAME to a claimable service Critical
1 check · ssrfProbe.ts

Out-of-band SSRF

A URL pointing back at a collector, carrying a token unique to one injection point, is planted in parameters whose names say they carry a URL. A callback proves the vulnerability — there is effectively no false positive, which is why it carries confidence 95.

Check Severity
Server fetches a URL supplied in a parameter Critical
1 check · pathTraversal.ts

Path traversal

URL parameters probed with encoded and double-encoded traversal payloads.

Check Severity
Path traversal / local file inclusion Critical
1 check · sourceMaps.ts

Source map exposure

Each JS bundle is checked for an adjacent .map file and for a sourceMappingURL comment pointing at a reachable one.

Check Severity
Source map publicly accessible High
1 check · apiDocsProbe.ts

API documentation exposure

Requires a parseable OpenAPI schema or a real Swagger UI bundle, not a keyword match. A found spec also feeds the API surface tests above.

Check Severity
OpenAPI / Swagger documentation exposed Medium
1 check · jsScanner.ts

JavaScript secret scanning

Inline scripts plus up to eight external files, 512 KB each, matched against 33 credential patterns with entropy thresholds. Public-by-design keys — pk_live_, sb_publishable_, Firebase AIza… — are never flagged on their own, and a JWT is only a credential leak when its payload carries an identity claim.

Check Severity
Credential found in JavaScript 33 patterns Varies
Not counted as checks

Signature tables

Four checks work by iterating a lookup table. Those 138 entries are signatures, not checks — /.env being exposed and /.git/config being exposed are one check, run twice. Counting them individually is how scanners advertise numbers that mean nothing.

TableEntriesSource
Sensitive paths probed 78 probes-data.ts
Secret patterns matched 33 secret-pattern-data.ts
Takeover fingerprints 19 subdomain-service-data.ts
Paths probed per crawled page 8 crawler-data.ts
Scoring

How the grade is calculated

Risk accumulates per finding, then maps to a letter. The weighting is deliberately steep — one Critical outweighs six Highs, because a single unauthenticated data read is not offset by good headers.

FindingPoints
Critical +30
High +15
Medium +5
Low +1
ScoreGrade
0–10 A
11–25 B
26–45 C
46–65 D
66+ F

The grade also drives rescan frequency if you turn on continuous monitoring — an A is rechecked every 14 days, an F every 3.

Scans are free while SecScan is in early access.

Run these 127 checks on your site →