Skip to content
SECQON

How SecQon keeps testing safe

A tool that sends attack traffic has to be safe by construction, not by promise. Here is how SecQon decides what it may touch, what it will never do, what it tells a language model, and how it records your consent — with the limits stated where they exist.

Nothing is tested until you prove you own it

Three ways to prove a domain, one for an IP

For a domain you add a DNS TXT record, host a small file at /.well-known/secqon-verification.txt, or add a meta tag to your homepage. For an IP address you host a token on it. Each token is a random 160-bit value tied to that asset.

The hosted-file and meta-tag checks refuse any redirect that leaves your host, so an open redirect somewhere cannot be used to prove ownership of someone else's site.

Checked four times, not once

Ownership is checked when the asset is added, when a scan is launched, again when the scan actually starts running, and once more when the scan's scope is built. If any target has lost its verification by then, the whole scan is blocked.

Re-verified every 30 days

Proof expires after 30 days. Before it does, we quietly re-run the check you originally passed and renew it. If the record or file has gone, the asset lapses, the change is logged, and it is not scanned again until you verify it afresh. We do not yet email you when that happens, so keep an eye on the asset's status.

A domain does not vouch for its subdomains

Verifying example.com does not put app.example.com in scope; each name verifies separately. Passive discovery reads certificate-transparency logs and passive DNS to suggest subdomains you may have forgotten, but it sends them nothing and adds nothing to scope. We do not brute-force DNS names. Our staff cannot verify an asset or launch a scan on your behalf.

A scope guard on every outbound packet

Checked at the moment of sending

When a scan starts, we build an allowlist from your verified assets, matched by exact host name. Before anything is sent to your asset — every request, handshake and connection — the target is checked against that allowlist at the moment it is sent. The check lives at the egress boundary, so no upstream code can skip it.

The allowlist is never widened by what a scan discovers. A host that turns up mid-scan, in a link, a redirect or a certificate, is refused before a single packet reaches it. That case has its own adversarial test.

Private and internal addresses are refused

The guard resolves the name and requires every returned address to be publicly routable. If any answer points somewhere private, the whole target is refused. That covers:

  • private (RFC 1918) ranges, loopback and link-local addresses — including 169.254.169.254, the cloud metadata address;
  • carrier-grade NAT, reserved and multicast ranges;
  • NAT64 prefixes, unconditionally.

Pinned to the address that was approved

The connection is made to the exact IP address the guard approved, and is never looked up again. TLS still validates the certificate for your host name. So a DNS answer that changes between the check and the connection — the trick known as DNS rebinding — cannot redirect us somewhere else.

Third-party tools we run, such as the template scanner, the crawler and the headless browser, go through a local proxy that applies the same guard and the same pinning. Two ways a headless browser could have slipped past it were found in our own testing and closed.

A backstop, and a Stop button that is honoured

Behind the guard, each scan's container sits behind a default-deny egress firewall. When you press Stop scan, the guard itself stops approving new traffic, so a sweep in progress stops sending. A request already in flight may still complete.

Destructive actions are blocked at every depth

Deny by default

Whatever the scan depth, including the full penetration test, the safety policy refuses the options that turn a testing tool into an attack tool. A refused option means the tool does not run at all, and the refusal appears in your report as a visible note rather than a silent skip.

  • No shells, command execution or file writes on your systems.
  • No bulk data extraction: the SQL-injection check detects and proves an injection without reading a row, and data-dump options are refused.
  • No denial-of-service or destructive templates.
  • No persistence and no lateral movement.

Bounded payloads, mostly read-only

Almost every request SecQon sends is a GET, a TLS handshake, a TCP connect or a DNS query. There are two bounded exceptions, both on Deep scans only: a single POST to at most three forms we can positively identify as search forms, to test how they echo input; and, on the full penetration test with a stored test account, an update to that account's own record with clearly marked probe values, to test mass assignment. The penetration test's active scan sends attack payloads, limited to an allowlist of 13 read-shaped rules; stored-XSS rules are deliberately excluded.

Every tool run also has hard ceilings: at most 25 requests in flight, 30 requests per second, and 200,000 requests.

Rate limits, and windows for scheduled intrusive scans

Ten requests a second per asset

Each asset has its own limit of 10 requests per second. A request over the limit is paced — held for up to eight seconds — rather than sent at once. Each account can run at most two scans at once.

Scans leave from a single reserved IP address, so you can recognise our traffic and allow-list it if you choose.

Scheduled intrusive re-audits run only in your window

Daily scheduled scans are always non-intrusive. If you authorise a monthly re-audit to run Deep or as a penetration test, it only runs inside a time window you choose — 01:00 to 05:00 UTC by default. Outside the window the re-audit waits; it is never quietly downgraded. Scans you launch yourself run when you launch them.

What the language model sees — and never sees

One job: plain-language explanations

The scan uses a language model for exactly one thing: rewriting a finding's explanation so someone without a security background can follow it. The model cannot change a finding's severity, CVE, CWE, OWASP category, exploitability label or remediation. Configuration fixes are hand-written, never generated. On the Free plan and on Light scans, no model is called at all.

The model runs in the same Google Cloud region as the rest of the service, Mumbai (asia-south1).

What the model is sent

  • The finding's category, title and severity
  • Its OWASP category, CWE and CVE, and which tool produced it
  • The request and response evidence, redacted and cut to 400 characters

What it never sees

  • Proof-of-concept transcripts, screenshots or raw scan data
  • Credentials you store for authenticated scanning — in any form
  • Authorization headers, API keys, tokens, passwords, private keys, cookies or email addresses: redacted by pattern before sending, and the model's reply is redacted again before it is stored

The model's wording is accepted only if it passes a content check: it may not mention any CVE other than the finding's own, introduce new numbers, add links or markup, or overstate the finding. Credentials you store for authenticated scanning are encrypted at rest, decrypted only in memory at the moment of sending, scoped to their host, and never appear in findings, evidence or logs.

An append-only, hash-chained audit log

Everything to do with authorisation is recorded

Each account has its own audit chain. Every entry is linked to the one before it by an HMAC-SHA256 hash, and a head record holds the latest hash and the count, so an edited entry or a trimmed tail shows up as a broken chain. It records:

  • acceptance of the Terms, Acceptable Use Policy and Rules of Engagement, by document version;
  • every scan attestation, with the Rules of Engagement version and its SHA-256 fingerprint;
  • Deep and authenticated-scanning acknowledgements;
  • scans started and completed, targets the scope guard refused, and every Stop;
  • any staff override, written into your account's own chain with a mandatory reason;
  • stored credentials being set or cleared — the header names only, never the values.

What it does not claim

The chain's anchor lives in the same database, so someone who compromised that whole database could rewrite both. We call the log append-only and hash-chained, not tamper-proof. It also does not record every individual guard decision, and scans that fail outright are not yet audited.

What we deliberately don't do

No scanning of anything you have not verified. Not a subdomain, not an address a crawl turned up, not a host in your API schema. Requests only ever go to verified hosts.

No brute force. We do not guess passwords, submit credentials to your login forms, or brute-force DNS names, directories or API endpoints. Checks for exposed files use short, fixed lists.

No full port sweeps and no UDP. Standard and Deep scans connect to a curated list of 36 TCP ports, close each connection immediately, and send nothing. A reachable port is reported as reachable — not as proof the service behind it is unprotected.

No business-logic or race-condition testing, no stored XSS, no attack chaining. These need a human tester or carry risks we will not take on your production systems.

No AI deciding what to attack. Each scan depth runs a fixed set of checks. The language model writes explanations; it does not plan or drive the test.

No invented findings. Every claim in a report maps to a tool result or a reproduction we performed. Where something could not be assessed, the report says so — including the two OWASP categories, Insecure Design (A04) and Security Logging and Monitoring Failures (A09), that no external test can assess.

Verify an asset and run a free scan