Documentation

How a scan works

SecScan is a black-box scanner: it sees your application the way an anonymous visitor does, plus whatever access you choose to give it. There is one kind of scan — all 127 checks, every time. What follows is the order it works in, and the rules governing what it will and will not send.

  1. 1

    Baseline

    One GET establishes the ground truth: response headers, cookies, detected technologies, and how the site answers a URL that cannot exist. That catch-all response is recorded before anything else runs, and every later probe is judged against it.

  2. 2

    Passive analysis

    Headers, cookies, JWTs already present in responses, __NEXT_DATA__ blobs, and DNS records are evaluated without sending anything further. A large share of all findings comes out of this stage at zero cost to the target — and this is the only stage that runs on an unverified domain.

  3. 3

    Active probes

    Specific paths, methods, origins and parameters get individual requests. Sensitive files, HTTP verbs, CORS behaviour, open redirects, directory listings, traversal payloads, injection canaries. Requires domain verification.

  4. 4

    Bundle and backend

    JavaScript bundles are fetched and read for secrets, source maps and API endpoints. Where a Supabase, Firebase, PocketBase or Appwrite project is found, its live API is tested for missing access rules.

  5. 5

    Confirmation

    Nothing is reported on a pattern match alone. A baseline is fetched, the probe is sent, and the finding stands only if the difference between them can be explained no other way.

  6. 6

    Report

    Findings accumulate a weighted risk score that maps to a letter grade, then an AI pass rewrites them in plain English and generates a fix prompt aimed at the stack you actually built with.

The domain-verification gate

This is the single most important thing to understand about your results, and the most common reason two scans look different.

Active probing does not run against a domain you have not proven you control. Until you verify, a scan runs the passive checks only — headers, cookies, DNS, JWTs, technology fingerprinting — and the report states plainly that active testing was skipped, rather than quietly returning a shorter list you might mistake for a clean bill of health.

Two ways to verify, either one is enough:

The gate fails closed. If the verification lookup errors — a database blip, a DNS timeout — the answer is "not verified", not "assume yes". Failing open here would turn the scanner into precisely the thing the gate exists to prevent: an anonymous attack proxy that anyone can aim at anyone.

This is friction on purpose. A scanner that will fire injection payloads at any URL a stranger pastes in is a weapon with a web form on it. The few minutes it takes to add a TXT record are what keep this a security tool.

Why false positives are the hard part

A scanner that reports everything it might have found is worse than no scanner. Twenty speculative findings bury the one real one, and after the second false alarm nobody opens the report again. Three techniques do most of the work.

Catch-all fingerprinting

Any application whose router answers unknown paths with the app shell — every single-page app, and multi-tenant platforms like GitHub or npm — returns 200 for /.env and for anything else you ask for. Recording the response to a deliberately nonexistent path first, then suppressing every probe hit whose body matches it, removes that entire false-positive class. Some paths get further confirmation on top: /phpmyadmin requires a real login-form marker, and an OpenAPI hit requires a parseable schema rather than the word "swagger" on a page.

Baseline diffing

SQL injection is confirmed by fetching a benign baseline, then the same parameter with a trailing quote, and requiring a SQL error signature that was absent from the baseline. A page that always contains SQL-error-looking text is therefore never flagged.

Random canaries

Reflected XSS is tested with a per-request random token wrapped in HTML metacharacters. The finding stands only if the literal token reflects back with its angle brackets unencoded. An encoded reflection — which is to say, correct output encoding — produces no finding at all.

The same discipline governs what is not flagged. A Supabase anon key, a Firebase AIza… key, a Stripe pk_live_ publishable key: all public by design, none ever reported as an exposed secret, no matter how many scanners report them as one.

Safety rules for active probing

Deep probing asks real questions of your application. Every request is bounded by one rule: nothing is sent whose success would change customer data. In practice:

Scanning behind a login

Most of an application lives behind auth, and a scan that never signs in never sees it. Two modes: replay a session cookie or bearer token you paste in, or sign in through a login page with a headless browser.

Credentials are encrypted at rest, decrypted only inside the worker for the life of one scan, discarded when it ends, attached only to requests aimed at the scan target, and dropped on any redirect that leaves it. If the session expires mid-scan the scanner signs back in where it can — a form login renews itself, a pasted cookie cannot — and the report says so either way, because a scan that silently lost its session covers less than you asked for.

Supplying a second account unlocks the access-control comparison. The same record is requested three ways — as you, as somebody else, and as nobody — and the responses compared by token-overlap similarity rather than byte equality, because real pages carry CSRF tokens and timestamps that differ on every render. The anonymous leg is what keeps public pages out of the report: a product page returns the same bytes to everyone, which without that third request is indistinguishable from a leak.

What a black-box scan cannot tell you

Being honest about the boundary is part of the value. An external scan does not read your source, does not reason about your business logic, and cannot tell you whether a given user should have access to a given record — only whether two different accounts get served the same one. It does not replace a manual penetration test for an application handling regulated data.

What it does is find the entire class of problems that are mechanical, externally visible and embarrassing: the config file you deployed, the table you never put a policy on, the key that ended up in a bundle, the certificate nobody renewed. Those are worth catching automatically and repeatedly, which is exactly what a human tester is too expensive to do.

Authorisation is your responsibility. Verify only domains you own or have written permission to test. Active probing against a third party without authorisation is unlawful in most jurisdictions and breaches most providers' terms of service.

See it against your own site

Scans run on secscan.us and are free during early access.

Start a scan →