What we check
Every check listed here runs automatically as part of a Security Score scan. Nothing is hidden behind “proprietary methodology” — this is the actual list, kept in sync with the scanner code.
// scan categories
Every check is read-only and passive by default — no exploit payloads, no destructive requests. The one exception is the optional ZAP active scan, which only runs where it's explicitly configured against a domain you've already verified you own.
TLS & Certificates
Runs on every scanConfirms traffic is actually encrypted, and that the certificate and protocol backing that encryption are sound.
- ›HTTPS enforced (not just available)
- ›Weak protocol detection (TLS 1.0/1.1 still enabled)
- ›Certificate expiry — flagged inside 21 days
- ›Self-signed certificate detection
- ›HSTS presence and max-age adequacy (≥180 days)
Security Headers & CSP
Runs on every scanChecks the response headers that stop a browser doing something dangerous with your content, even after everything else is right.
- ›OWASP-recommended header set (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy…)
- ›Content-Security-Policy graded A–F — unsafe-inline, unsafe-eval, and wildcard sources all cost points
- ›Cookie flags: Secure, HttpOnly, SameSite
- ›CORS wildcard origin (Access-Control-Allow-Origin: *)
- ›Server/technology version disclosure in headers
DNS & Email Authentication
Runs on every scanWhether your domain can be spoofed for phishing, and whether your certificate issuance is locked down.
- ›SPF record present and correctly scoped
- ›DKIM key discovery across the default selectors used by major providers, plus key strength, revocation, and testing-mode checks
- ›DMARC record present, and enforcement policy (none / quarantine / reject)
- ›MTA-STS policy published and enforced, for domains that receive mail
- ›TLS-RPT reporting address configured, so failed mail-TLS connections are visible
- ›CAA records restricting which CAs can issue certificates for your domain
- ›DNSSEC signing status
Sensitive File Exposure
Runs on every scanProbes for the specific files that leak credentials or source code when a deployment forgets to exclude them — with false-positive hardening (see below) so a catch-all SPA doesn't get flagged for everything.
- ›Git/SVN metadata (.git/HEAD, .git/config, .svn/entries)
- ›Environment files (.env, .env.production, .env.backup)
- ›Framework config (config.php, wp-config.php.bak, settings.py, database.yml)
- ›Backup archives and raw SQL dumps
- ›Debug/admin pages (phpinfo, server-status, Symfony profiler, Laravel Telescope)
- ›Credential files (.htpasswd, .htaccess) and application logs
- ›security.txt presence (credited, not penalised, when found)
Directory Listing (Autoindex)
Runs on every scanLooks for Apache/nginx directory browsing left switched on for uploads, backups, and log folders — scoped narrowly to genuine autoindex pages (an "Index of /…" title plus a Parent Directory link), not just any page that happens to load at a common path.
- ›/uploads/, /backup/, /files/, /media/, /downloads/ and similar common paths
- ›/logs/, /tmp/, /data/, /wp-content/uploads/
- ›Approximate file count when a listing is found
Cloud Storage Misconfiguration
Runs on every scanExtracts S3, Azure Blob, and GCS references from your own page source, then tests only those specific buckets for public listing. Never guesses bucket names — that would mean scanning infrastructure you don't own.
- ›S3 buckets referenced in your site — tested for public ListBucketResult
- ›Azure Blob containers — tested for public EnumerationResults
- ›GCS buckets — tested via the same S3-compatible listing check
Technology Fingerprinting
Runs on every scanPassive, Wappalyzer-style identification of your stack from headers and page source — no active probing, just pattern matching on what's already public.
- ›40+ frameworks, CMSes, servers, CDNs, and third-party services detected
- ›Version extraction where the response reveals one
- ›Known-outdated major versions flagged (jQuery, Bootstrap, WordPress, Angular, PHP, Apache)
Open Ports & Network Exposure
Runs on every scanChecks which ports are visible to the internet using Shodan's InternetDB — no port-scanning traffic sent to your infrastructure directly.
- ›Administrative and database ports that shouldn't be public (SSH, RDP, MySQL, PostgreSQL, MongoDB, Redis)
- ›Risk-rated per port with the reason it matters
Headless Browser & Page Health
Runs on every scanActually loads the page in a real browser engine, the way a visitor would — catches problems a plain HTTP fetch can't see.
- ›Page load status and health
- ›Mixed content — HTTP resources loaded on an HTTPS page
- ›Browser console errors
Reconnaissance
Runs on every scanPassive intelligence gathering from public sources — nothing sent to your infrastructure beyond a standard page request.
- ›CVEs Shodan associates with your IP's exposed services
- ›Subdomains discovered via Certificate Transparency logs (crt.sh)
Web Application Security
Runs on every scanA grab-bag of checks that don't fit a single category but catch some of the most common real-world misconfigurations.
- ›robots.txt reviewed for sensitive disallowed paths it inadvertently advertises
- ›Dangerous HTTP methods enabled (PUT, DELETE, TRACE)
- ›Error pages that leak stack traces or framework internals
- ›CORS reflected-Origin misconfiguration (with or without credentials allowed)
- ›JavaScript library versions (jQuery, Moment.js, Lodash, Bootstrap, AngularJS…) matched against live CVE data from OSV.dev
- ›Cross-origin scripts and stylesheets loaded without a Subresource Integrity hash
CMS-Specific Deep Scans
Runs when detectedOnce fingerprinting identifies WordPress, Joomla, or Drupal, a second pass runs checks specific to that platform.
- ›WordPress: XML-RPC exposure, REST API user enumeration, debug.log, readme.html version disclosure, wp-cron.php
- ›Joomla: core version disclosure via the administrator manifest, leftover installer files
- ›Drupal: core version disclosure via CHANGELOG.txt, update.php reachability, public JSON:API, uid 1 profile enumeration
Active Application Scanning (OWASP ZAP)
Optional (ZAP)Where a ZAP instance is configured, an active spider/scan can be triggered against the target for deeper coverage than passive checks alone. Off by default — active scanning sends real attack payloads, so it only runs where explicitly enabled for a verified target.
- ›Spidering and active vulnerability scanning via a connected ZAP daemon
Authenticated Scanning
Optional (Agency)Connect a session cookie or Basic Auth credential for a monitored site and the same header/cookie/CSP/CORS checks re-run against the authenticated page — not a full crawl behind login, and not access-control (IDOR) testing. The one thing this uncovers that nothing else in this suite can: a real session cookie with insecure flags, which is invisible until you're actually logged in.
- ›Missing security headers on the authenticated page
- ›Session cookies missing Secure/HttpOnly/SameSite — graded more seriously than the same issue on a public page
- ›CSP and CORS misconfiguration on authenticated responses
How every finding is scored and explained
Running the checks is half the product — turning results into something you can act on is the other half.
Severity scoring
Every finding is rated critical, high, medium, low, or info, and rolled up into a single score out of 100 so you can track improvement over time.
- ›Critical/high/medium/low/info severity on every finding
- ›0–100 score with weighted penalties per severity
- ›Score trend chart across every past scan of the same target
Live CVE references
Where a finding is a known-vulnerable JS library, we look up real CVE IDs from OSV.dev at scan time rather than relying on a hardcoded list that goes stale.
- ›Live OSV.dev lookup for vulnerable JS libraries
- ›Checks both direct advisory IDs and GHSA aliases
- ›Falls back gracefully — no CVE claimed if OSV.dev is unreachable
Copy-pasteable remediation
Every finding type ships with specific fix instructions — config snippets for nginx, Apache, Next.js, and Azure Static Web Apps where relevant, not generic advice.
- ›Per-finding Markdown remediation with real config snippets
- ›Automated PRs available — findings become an actual pull request with a SECURITY-FIXES.md
- ›AI-tailored guidance available on request for your specific detected stack
Scan-to-scan comparison
Every finding carries a stable identity, so re-scanning the same target shows exactly what's new and what's been fixed — not just a fresh, unrelated list.
- ›New vs. fixed vs. unchanged, compared against your last scan
- ›Shareable, read-only report links for clients or auditors
- ›Exportable PDF reports
How we avoid false positives
The single biggest failure mode for this kind of scanning is a catch-all site that returns 200 for every URL — naively probed, that looks like every sensitive file and every admin panel exists. Here's specifically what stops that.
Known-404 baseline first
Before probing anything real, every path-probing check fingerprints the site root and a random, guaranteed-nonexistent URL. If that random URL returns 200, the site serves a catch-all — and every subsequent probe gets compared against that baseline, not treated as a finding on its own.
- ›Shared across every path-probing check (src/lib/scanners/httpProbe.ts) — one proven implementation, not a copy per scanner
- ›ETag / Content-Length compared against the site root as a first, cheap signal
Path-masked body comparison
Catch-all shells often echo the requested URL back into the page (a router, a canonical link, an error message) — which would otherwise make every probed path look like a different document, and can accidentally satisfy a content check by pure coincidence. The probed path is masked out of both bodies before they're ever compared.
- ›Prefix/suffix similarity match on the masked bodies — ≥85% shared content counts as "the same page"
- ›Applied identically whether the probe is for a leaked .env file or an open directory listing
Content assertions, not just status codes
A 200 response alone is never enough to call something exposed. Each check asserts the body actually looks like what it claims to have found.
- ›A .env "hit" must match a real KEY=value assignment pattern, not just any 200
- ›A directory listing must show both an "Index of /..." title and a Parent Directory link — a page that merely loads at /uploads/ (an upload form, say) doesn't count
- ›A .htpasswd "hit" must match a real password-hash line format
Scoped to evidence we actually have
Every check only reports what it can back up — no guessing at what "might" be there.
- ›Cloud misconfig only tests buckets the target's own page source references — never guesses bucket names against infrastructure the customer doesn't own
- ›CVE references come from a live OSV.dev lookup against the exact detected version — no CVE claimed if the lookup fails or nothing matches
- ›CMS deep scans (WordPress/Joomla/Drupal) only run once fingerprinting has actually detected that CMS
What this can't detect
Automated scanning is genuinely good at what's listed above, and genuinely bad at everything below. Worth knowing before you treat a clean scan as a clean bill of health.
- ▸Business logic flaws — price manipulation, privilege escalation through a legitimate workflow, coupon/quantity abuse. These require understanding what the application is supposed to do, which no automated scan can infer.
- ▸Deep authenticated testing — every scan here is unauthenticated (black-box against the public site) by default. Connecting a session cookie or Basic Auth credential (Agency plan) re-runs header/cookie/CSP/CORS checks against the logged-in page, but that's a narrow recheck, not a crawl behind login. Access-control bugs that only show up once you're logged in as one user trying to reach another user's data (IDOR), and multi-step auth flows (OAuth, MFA), aren't covered.
- ▸Zero-days and unknown vulnerabilities — nuclei and the fingerprinting checks match against known template/CVE databases. Anything not yet publicly known or catalogued won't be flagged.
- ▸DKIM on a custom selector — a DKIM key lives at a selector name chosen freely by whoever set it up, and it cannot be discovered from DNS. We probe the defaults the major providers use, so finding a key is real evidence, but not finding one isn't proof of absence. We report that case as informational, never as “DKIM missing”.
- ▸Real active scanning, most of the time — nmap/nuclei/sqlmap/XSSer/commix/a real ZAP active scan only run on the scheduled monitoring path, where domain ownership has already been verified. An on-demand dashboard scan is passive/read-only checks only.
- ▸Physical, social, and process risk — phishing susceptibility, insider threats, physical access controls, offline backup hygiene. None of that is visible from the outside of a website.
For the things on this list, see manual penetration testing — a human tester, not a scanner, is what actually finds these.
// ready?
See it run against your own site
Free scan, one-page report in 24 hours, no commitment.
Run a free scan