How the score is computed
Every finding contributes a deduction from the starting 100 points based on its severity:
| Severity | Base deduction |
|---|---|
| critical | -15 |
| high | -8 |
| medium | -3 |
| low | -1 |
| info | 0 |
Multipliers stack on top of the base deduction:
- ×1.5 when
exploit_availableis set — Public PoC / Metasploit module / known-exploited-in-the-wild flag - ×1.2 when
chainedis set — Finding contributes to a multi-step attack chain
To prevent a single bad category from sinking the whole score, no single category may deduct more than 60 points. The remaining 0-100 score maps to letter grades:
| Grade | Score range |
|---|---|
| A+ | ≥ 95 |
| A | 85-94 |
| B | 70-84 |
| C | 50-69 |
| D | 30-49 |
| F | 0-29 |
TLS / certificates
Probes the TLS termination at port 443: which protocols are accepted, which cipher suites, certificate validity and chain trust, Heartbleed, and forward secrecy.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-001 |
Weak TLS/SSL protocols enabled | medium-high | Server accepts deprecated TLS 1.0/1.1 (or worse: SSL 2.0/3.0). Vulnerable to POODLE, BEAST, FREAK and similar downgrade attacks. | Disable everything below TLS 1.2. nginx: `ssl_protocols TLSv1.2 TLSv1.3;`. |
L1-002a |
Weak cipher suites accepted | medium | Server still negotiates RC4, 3DES, EXPORT-grade or NULL ciphers. | Restrict to AEAD: TLS_AES_128_GCM_SHA256, ECDHE+AESGCM family. |
L1-002b |
No forward-secrecy ciphers | medium | All accepted ciphers use static RSA. If the private key leaks, all past recorded sessions can be retroactively decrypted. | Configure ECDHE cipher suites; disable plain RSA key exchange. |
L1-001b |
Modern TLS not supported | high | Server does not negotiate TLS 1.2 or 1.3 at all — clients are forced onto deprecated protocols or can't connect securely. | Enable TLS 1.2 and 1.3. nginx: `ssl_protocols TLSv1.2 TLSv1.3;`. |
L1-003 |
TLS certificate expired | critical | The leaf certificate's not_after date is in the past. | Renew immediately; verify auto-renewal (certbot, ACME) is healthy. |
L1-003d |
Weak certificate key | medium-high | The certificate's public key is below modern strength (e.g. RSA < 2048-bit or a small/again-deprecated curve), weakening the whole TLS handshake. | Reissue with RSA ≥ 2048-bit (2048/3072) or an ECDSA P-256/P-384 key. |
L1-003e |
OCSP stapling not enabled | info | The server doesn't staple an OCSP revocation response into the TLS handshake, so clients must contact the CA separately — slower, and it leaks visitor browsing to the CA. A hardening gap, not a vulnerability (info-only, no score impact). | Enable OCSP stapling (nginx `ssl_stapling on; ssl_stapling_verify on;` with a resolver); consider OCSP Must-Staple on the certificate. |
L1-003a |
TLS certificate expires soon | high | Certificate expires within 14 days. | Force a renewal now; investigate why auto-renewal didn't fire. |
L1-003c |
Certificate chain not trusted | high | Chain failed validation against the system trust store. Browsers show a warning and most clients refuse to connect. | Configure the full intermediate chain; use a CA in the public trust store. |
L1-004 |
Heartbleed (CVE-2014-0160) | critical | Server vulnerable to Heartbleed memory disclosure. | Upgrade OpenSSL ≥ 1.0.1g; revoke and reissue all TLS certificates. |
HTTP security headers
Inspects the headers on the homepage response: HSTS, CSP, framing, MIME sniffing, referrer policy, permissions, cookies, server disclosure, CORS.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-005 |
Missing HSTS | medium | No Strict-Transport-Security header. Browsers may downgrade to HTTP if a user types the bare hostname. | Add: `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload`. |
L1-005a |
HSTS max-age too short | low | max-age below 31536000 (1 year) — preload list ineligible. | Increase max-age to ≥ 31536000. |
L1-005b |
HSTS missing includeSubDomains | low | Subdomains not protected. | Add the includeSubDomains directive. |
L1-005c |
HSTS not on the preload track | low | HSTS is set but not preload-eligible (missing `preload`, or max-age/includeSubDomains don't meet the preload-list requirements), so the very first visit to a bare hostname can still be downgraded. | Meet the preload rules (max-age ≥ 31536000; includeSubDomains; preload) and submit at hstspreload.org. |
L1-006 |
Missing Content-Security-Policy | medium | No CSP header — the page has no scripted content allowlist. | Start with `default-src 'self'; script-src 'self'; object-src 'none'`. |
L1-006a |
CSP allows 'unsafe-inline' | medium | CSP includes 'unsafe-inline' — defeats most XSS protections. | Move inline scripts/styles to external files; or use nonces/hashes. |
L1-006b |
CSP allows 'unsafe-eval' | medium | eval() and Function() constructors are allowed. | Refactor any code that depends on eval; remove the directive. |
L1-006c |
CSP missing default-src | low | Without default-src, missing directives fall through to '*'. | Add `default-src 'self'` as the backstop. |
L1-006d |
CSP wildcard sources in critical directives | medium | `*` or `*.domain` allows scripts from any origin matching the wildcard. | Replace wildcards with specific origins. |
L1-006e |
CSP missing frame-ancestors | low | Without frame-ancestors, only the legacy XFO header guards against framing. | Add `frame-ancestors 'none'` (or `'self'`). |
L1-006f |
CSP doesn't restrict object/embed | low | No `object-src` (and no default-src backstop) — legacy `<object>`/`<embed>`/Flash-style plugins can still load arbitrary content. | Add `object-src 'none'`. |
L1-007 |
No clickjacking protection | medium | Neither X-Frame-Options nor CSP frame-ancestors set. | Add CSP `frame-ancestors 'self'` (preferred) or `X-Frame-Options: SAMEORIGIN`. |
L1-008 |
Missing X-Content-Type-Options | low | Browsers may MIME-sniff resources, enabling some XSS via uploads. | Add `X-Content-Type-Options: nosniff`. |
L1-009 |
Missing Referrer-Policy | low | Browsers leak full URLs (incl. query strings) on cross-origin navigation. | Add `Referrer-Policy: strict-origin-when-cross-origin`. |
L1-009a |
Weak Referrer-Policy | low | A Referrer-Policy is set but to a permissive value (e.g. `unsafe-url`) that still leaks full URLs cross-origin. | Use `strict-origin-when-cross-origin` or `no-referrer`. |
L1-010 |
Missing Permissions-Policy | low | Browser features (camera/microphone/geolocation) aren't restricted. | Add `Permissions-Policy: geolocation=(), camera=(), microphone=()`. |
L1-011 |
Cookie flag issues | low-medium | Cookies set without Secure / HttpOnly / SameSite (or unsafe combos). | Set `Secure; HttpOnly; SameSite=Lax` (or Strict) on session cookies. |
L1-012 |
CORS misconfiguration | low-high | ACAO=`*` with credentials (impossible/invalid) or wildcard on auth endpoints. | Echo a specific origin against an allowlist; never `*` with credentials. |
L1-012a |
CORS allows any origin | medium | Access-Control-Allow-Origin is `*` (or reflects any Origin), so any site can read responses from this endpoint in a browser context. | Restrict ACAO to an explicit allowlist of trusted origins. |
L1-030 |
Server version disclosed | low | The `Server` header reveals the exact server/version, helping attackers match known CVEs faster. | Suppress version banners (nginx `server_tokens off;`). |
L1-030a |
X-Powered-By disclosed | low | `X-Powered-By` (or `X-AspNet-Version`) leaks the framework/runtime version. | Remove the header (e.g. Express `app.disable('x-powered-by')`). |
DNS / email hygiene
Queries the apex domain for SPF, DMARC, DKIM (selector probes), DNSSEC, MTA-STS, CAA, BIMI, and probes for open zone transfer.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-015 |
No SPF record | medium | No `v=spf1` TXT record. Email from this domain can be spoofed without effort. | Publish SPF, e.g. `v=spf1 include:_spf.google.com -all`. |
L1-015a |
SPF uses permissive `all` qualifier | medium | SPF ends with `+all` or `?all` — anyone passes. | Use `-all` (hardfail). |
L1-016 |
DKIM not detected at common selectors | low | We probed 14 common selectors and found none. Either DKIM isn't set up or you use an uncommon selector. | Configure DKIM at your mail provider; supply selector via API to verify. |
L1-017 |
No DMARC | high | No `v=DMARC1` record at _dmarc. No policy enforcement against spoofing. | Start with `p=quarantine` + `rua=mailto:dmarc@yourdomain`. |
L1-017a |
DMARC `p=none` (monitor only) | medium | Spoofed mail is reported but not blocked. | Move to p=quarantine then p=reject when reports look clean. |
L1-018 |
DNSSEC not enabled | low | No DS/DNSKEY records. DNS responses can't be cryptographically validated. | Enable DNSSEC at your registrar / DNS provider. |
L1-019 |
No MTA-STS policy | low | Email between mail servers can downgrade to plaintext. | Publish MTA-STS TXT + serve policy at https://mta-sts.<domain>/.well-known/mta-sts.txt. |
L1-020 |
No BIMI record | info | Verified logo can't appear in inbox previews — small anti-phishing signal. | Get a Verified Mark Certificate; publish BIMI record (requires DMARC ≥ quarantine). |
L1-021 |
Open zone transfer (AXFR) | high | Authoritative server allowed an AXFR — every DNS record in the zone is dumpable. | Restrict AXFR to specific allowed slaves only. |
L1-022 |
No CAA record | low | Any CA in the public trust store can issue a cert for this domain. | Publish `domain CAA 0 issue "yourca.com"` to pin issuance. |
Tech stack fingerprint
Reads response headers and HTML markers to identify the server, framework, and CMS in use. Heuristic CVE flags when a known-old version is fingerprintable.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-023 |
Tech stack fingerprint | info | Informational — what we detected (Server header, X-Powered-By, meta-generator). | Suppress version strings: nginx `server_tokens off`; remove X-Powered-By. |
L1-024 |
CMS / framework detected | info | Identified a CMS or application framework (WordPress, Drupal, Joomla, Laravel, Django, …). Informational — deeper CMS checks run at Layer 2. | Keep the platform and all plugins/themes patched; restrict admin paths. |
L1-025 |
Outdated component with public CVEs | medium | Detected version is below our last-known-safe threshold. | Upgrade to your distro's latest patched version. |
L1-026 |
Detected version matches catalogued CVE(s) | medium-critical | The fingerprinted version falls inside the affected range of one or more catalogued CVEs (each with CVSS, a CISA-KEV flag, and an NVD link). Version banners can be stale or backported, so each finding is framed as 'verify against your SBOM / NVD'. The CVE catalogue is refreshable. | Confirm the version against your SBOM, then upgrade past the fixed release; prioritise anything flagged CISA-KEV or exploit-available. |
Information disclosure
Probes a curated list of paths that should never be public: source-control leftovers, backups, config files, debug endpoints, IDE configs.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-040 |
Source/config file leaked | high-critical | .env, .git/config, .git/HEAD, .htpasswd, dump.sql, .DS_Store etc. publicly readable. | nginx: `location ~ /\. { deny all; }` and `location ~ \.(sql|bak|swp)$ { deny all; }`. |
L1-041 |
Admin interface exposed | low-medium | Default admin path (/admin, /phpmyadmin, /wp-admin) reachable without auth challenge. | Move admin behind VPN or IP allowlist; rename the path. |
L1-042 |
Debug/status endpoint exposed | medium-high | Apache mod_status, Spring Actuator, /metrics, /debug/vars publicly readable. | Bind these endpoints to localhost; or require authentication. |
L1-043 |
Directory listing enabled | medium | An index page exposes file names in the directory. | nginx `autoindex off`; Apache `Options -Indexes`. |
L1-044 |
Backup file exposed | high-critical | /backup.zip, /db.sql, /www.tar.gz publicly downloadable. | Move backups outside the web root; deny access to common backup names. |
L1-045 |
Hard-coded secret in client-side code | high-critical | A secret-shaped value (Stripe/AWS/SendGrid/Twilio/GitHub key, JWT, private key, DB URI, etc.) appears in the homepage or its first-party JavaScript. Anything shipped to the browser is public. Matched values are masked — the raw secret is never stored. | Move secrets server-side behind an API; use short-lived scoped tokens; rotate anything surfaced here and lock keys to your domains. |
Subdomain enumeration + takeover
Probes ~30 common subdomain prefixes; for each that resolves to a known-third-party service, checks whether that target is dangling (unclaimed) — a takeover candidate.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-050 |
Possible subdomain takeover | high | A subdomain CNAMEs to a third-party service (S3/Heroku/GitHub Pages/Azure/etc.) but the resource at that target doesn't resolve. An attacker who claims it can host arbitrary content under your subdomain. | Either reclaim the resource at the upstream service, or remove the CNAME. |
L1-051 |
Internal-looking subdomains reachable | low | Subdomains with prefixes like `dev.`, `uat.`, `staging.`, `admin.` are publicly resolvable. | If meant to be internal: VPN / IP allowlist / authentication / private DNS only. |
Recon / external intelligence
The reconnaissance phase, run on verified-owner deep scans: maps the target's external attack surface from public intelligence sources WITHOUT touching the target. Certificate Transparency is keyless; Shodan and HaveIBeenPwned activate when the operator supplies an API key.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-037 |
Subdomains via Certificate Transparency | info-low | Public CT logs (crt.sh) record every TLS certificate ever issued for the domain, revealing subdomains — including non-production and admin hosts that DNS brute-forcing misses. Flagged low when privileged/non-prod names appear. | Inventory every name; put non-prod / admin surfaces behind VPN, IP allowlist or SSO; decommission stale hosts. Names in CT logs are permanently public. |
L1-038 |
Exposed ports / services / host CVEs (Shodan) | medium-high | Shodan's internet-wide scan data lists ports, service banners and correlated CVEs for the resolved host — observed without CamsClaw touching it. High when known CVEs are present. Requires an operator-supplied Shodan API key. | Firewall ports that need not be public; patch CVE-affected services; place them behind a WAF / bastion. |
L1-039 |
Domain breach exposure (HaveIBeenPwned) | high | HaveIBeenPwned associates known data breaches with the domain's email addresses. Leaked corporate credentials are the most common initial-access vector via credential stuffing. Requires an operator-supplied HIBP API key. | Force resets on affected accounts, enforce MFA, check for credential reuse in auth logs, and subscribe the domain to HIBP notifications. |
WAF / edge
Identifies any in-front edge provider via response-header fingerprints (Cloudflare, Akamai, AWS, Imperva, F5, Fastly, Sucuri, Azure, Google).
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-060 |
WAF / edge detected | info | Edge provider identified — informational context for other findings. | — |
L1-061 |
No WAF detected | info | Origin appears to face the public internet directly. | Consider Cloudflare/Akamai/AWS WAF for DDoS + OWASP-pattern protection. |
Privacy (GDPR / DPDPA)
Inventories third-party tracking domains loaded by the homepage and detects whether a consent banner is shown before tracking starts.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-070 |
No cookie consent banner detected | medium | Trackers present but no consent UI fingerprint detected on initial render. | Add a CMP (OneTrust, Cookiebot, Iubenda, etc.) — consent before tracking. |
L1-071 |
Trackers load before consent | medium | Banner present but tracker scripts execute on initial page render. | Configure consent gating (Consent Mode v2 in GTM; auto-blocking in OneTrust). |
L1-072 |
Third-party tracker inventory | info-low | List of known trackers loaded — analytics, ads, session replay, etc. | Audit each — consent-gate any not strictly necessary. |
Authentication surface
Inspects the public auth surface: login form HTTPS, autocomplete, CSRF token presence, HTTP Basic auth detection. **No credentials are ever submitted.**
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-080 |
Login form not HTTPS | high | Login page or form action is HTTP — credentials sent in cleartext. | Serve the login page over HTTPS; ensure form action is HTTPS. |
L1-081 |
Password field allows browser autocomplete | low | Password input doesn't set `autocomplete="current-password"` or `"new-password"`. | Set explicit autocomplete values on password inputs. |
L1-082 |
Login form has no visible CSRF token | low | Form has no hidden input matching common CSRF token names (csrf, _token, ...). | Use server-generated CSRF tokens, or rely on SameSite=Strict cookies + custom headers. |
L1-083 |
HTTP Basic authentication on root | medium | Root URL challenges with HTTP Basic auth — credentials transmit base64, no rate limits. | Use a proper login form + session cookies; or move behind VPN/IP allowlist. |
API surface
Probes for OpenAPI/Swagger documentation exposure and GraphQL introspection.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-090 |
API documentation exposed | low | OpenAPI/Swagger spec or UI publicly accessible. OK if intentional (Stripe-style); not OK if internal API. | If internal: restrict /swagger paths to authenticated users. |
L1-091 |
GraphQL introspection enabled | medium | GraphQL endpoint accepts introspection queries — full schema accessible. | Disable introspection in production. Apollo: `introspection: false`. |
Mixed content + SRI
Loads the homepage HTML and looks for HTTP resources on HTTPS pages, and external scripts/stylesheets missing Subresource Integrity hashes.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-100 |
Mixed content (HTTP on HTTPS) | medium | HTTPS page loads images/scripts/iframes over plain HTTP. | Update all references to HTTPS, or use protocol-relative URLs (`//domain/...`). |
L1-101 |
External resources missing SRI | low | External `<script>`/`<link>` without `integrity=` attribute. If the CDN is compromised, attackers can ship code without browser warnings. | Add SRI: `<script src="..." integrity="sha384-..." crossorigin="anonymous">`. |
AI / LLM attack surface
CamsClaw's AI-native checks — exposed AI infrastructure, leaked model-provider keys, agent/MCP endpoints, RAG vector stores, and downloadable model artifacts. Each hit is confirmed by a service-specific response signature (no generic-200 false positives). Mapped to the OWASP LLM Top 10 (2025), MITRE ATLAS, and NIST AI RMF.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L1-110 |
Exposed LLM inference API | high | An unauthenticated LLM-serving endpoint is reachable (Ollama, OpenAI-compatible `/v1`, HuggingFace TGI, etc.) — anyone can run inference on your compute. | Require auth / network-restrict the endpoint; never expose model servers to the public internet. |
L1-111 |
Exposed AI notebook / app surface | high | A Jupyter, Gradio, Streamlit, ComfyUI, Langflow or Open WebUI surface is reachable — several allow arbitrary code execution or custom-node code paths. | Put these behind SSO / VPN; disable public binding; set authentication tokens. |
L1-112 |
Exposed ML lifecycle / orchestration | high | An MLflow, Ray, or NVIDIA Triton control/management surface is publicly reachable. | Restrict to private networks; enable authentication; firewall the management ports. |
L1-113 |
Exposed vector database | high | A Weaviate / Qdrant / Chroma vector store answers unauthenticated — the data layer behind your RAG/embeddings is internet-facing. | Enable auth (API keys/tokens); network-restrict the database; never expose it directly. |
L1-114 |
Leaked AI provider API key in client code | high-critical | An OpenAI/Anthropic/HuggingFace/Google/Groq/Replicate/xAI key appears in the homepage or first-party JS. Matched values are masked — never stored. Anyone can spend on your account. | Move model calls server-side; rotate the leaked key immediately; scope keys minimally. |
L1-115 |
AI plugin / agent manifest exposed | low | An `ai-plugin.json` / `/.well-known/ai-plugin.json` manifest is published, describing agent capabilities and endpoints (recon value, sometimes unintended). | Confirm the manifest is intentional; remove it if the plugin/agent isn't a public product. |
L1-116 |
Model Context Protocol (MCP) endpoint exposed | medium-high | An MCP endpoint responds; if it lists its tool catalogue to an anonymous caller it's elevated (high when dangerous shell/SQL/deploy-style tools are present) — the agentic attack surface OWASP flags under excessive agency. | Authenticate the MCP transport; don't expose tool listing publicly; minimise tool scope. |
L1-117 |
AI-crawler governance in robots.txt | info | Whether robots.txt addresses AI crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot, PerplexityBot, Bytespider, …). Informational — a content-governance signal. | Decide your policy and set explicit `User-agent` rules for AI crawlers in robots.txt. |
L1-118 |
Vector-DB collection catalogue exposed | high | An exposed vector store also lists its collection names unauthenticated — concrete RAG data exposure (OWASP LLM08). Reads names only, never vectors/documents. | Require auth on the vector DB; network-restrict it; treat collection metadata as sensitive. |
L1-119 |
Exposed model artifact | high-critical | A model file is downloadable (pickle/PyTorch → critical untrusted-deserialization RCE; GGUF/HDF5/safetensors/ONNX → weight/IP theft). Confirmed by magic bytes via a 64-byte ranged GET — the model is never downloaded. | Move model artifacts out of the web root; serve from authenticated storage; never expose pickles. |
Layer-2 authenticated (behind login)
Verified-owner-only checks: with a test session you supply, CamsClaw replays it to cover the post-login surface a logged-out scan can't see. GET-only and passive — same-origin, logout-safe, bounded. Active behind-login testing (IDOR / access control / injection) is Layer-3.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L2-001 |
Authenticated crawl | info | Inventories the logged-in pages reached with the supplied test session — the behind-login attack surface. Confirms the session worked, or reports if it bounced to a login page. | Review that every page reached is meant to be visible to this account; pair with a Layer-3 active scan for cross-account IDOR / access-control testing. |
L2-002 |
Authenticated session-cookie hygiene | medium | Session cookies set after login that lack Secure / HttpOnly / SameSite — only visible behind authentication, which is why a passive logged-out scan can't see them. | Set `Secure; HttpOnly; SameSite=Lax` (or Strict) on all session cookies. |
L2-003 |
Authenticated page is cacheable | low | A post-login page didn't send `Cache-Control: no-store` (or `private`); sensitive content can be retained by shared caches or recovered via the browser back button on shared devices. | Send `Cache-Control: no-store` (or `private, no-cache`) on authenticated responses. |
Layer-3 active testing (consented)
Runs ONLY after the owner proves domain control AND gives explicit, versioned consent. Probes are detection-grade and reputation-safe: throttled, request-budgeted, source-IP identifiable, and they deliberately EXCLUDE denial-of-service, brute-force, and data exfiltration. Every request is audit-logged for a defensible record. XSS/SQLi use opaque canaries (not live scripts), SSRF is out-of-band only, XXE never reads files or hits SSRF.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L3-001 |
Reflected XSS (canary) | critical | Injects opaque canary tokens (never live `<script>`) into parameters and confirms verbatim reflection in an HTML context. | Context-encode all output; add a strict CSP; validate/allowlist input. |
L3-002 |
Error-based SQL injection | critical | A single quote that breaks query syntax produces a database error absent from the control response. | Use parameterised queries / prepared statements; never concatenate input into SQL. |
L3-021 |
Boolean-blind SQL injection | high | `AND 1=1` vs `AND 1=2` response-size oracle with a confirmation round (no OR-based auth bypass, no data read). | Parameterise queries; add a WAF as defence-in-depth. |
L3-018 |
NoSQL injection | high | Benign syntax breakers elicit MongoDB/Mongoose/CouchDB/JSON.parse error signatures. | Validate types server-side; reject operator objects in user input. |
L3-017 |
OS command injection | critical | A read-only `id` behind shell separators returns a `uid=…gid=…` signature that input reflection can't produce. | Never pass user input to a shell; use exec APIs with argument arrays + allowlists. |
L3-006 |
Server-side template injection | critical | `{{7*191}}`/`${7*191}` between unique sentinels evaluates to 1337 — proving template evaluation, not reflection. | Don't render user input as a template; sandbox the engine; use logic-less templates. |
L3-019 |
XML external entity (XXE) | high | Internal-entity expansion only (never SYSTEM/file/SSRF), on endpoints that already speak XML. | Disable external entities and DOCTYPE processing in the XML parser. |
L3-020 |
CRLF / response-header injection | high | A single opaque marker header is reflected back (server never emits it on its own); never splits the body. | Strip CR/LF from values placed into response headers / redirects. |
L3-007 |
Path traversal / LFI | high | `../` and bypass variants matched only against world-readable, non-sensitive files (e.g. /etc/passwd marker). | Resolve + confine paths to a base dir; reject `..`; use opaque file IDs. |
L3-005 |
Open redirect | medium | A reserved-TLD canary in redirect params is detected via the Location header (never followed). | Allowlist redirect targets; never redirect to a raw user-supplied URL. |
L3-004 |
CORS misconfiguration | medium-critical | An evil/`null` Origin is sent; reflecting it (especially with credentials) is the finding. | Echo only allowlisted origins; never combine `*`/reflected origin with credentials. |
L3-010 |
Host-header injection | medium | `X-Forwarded-Host`/Host canary reflected in a Location header or absolute URL (cache/reset poisoning). | Validate Host against an allowlist; build absolute URLs from a fixed canonical host. |
L3-008 |
GraphQL introspection exposure | medium | A spec introspection query returns the full schema (read-only, no mutations). | Disable introspection in production. |
L3-009 |
HTTP method posture (OPTIONS/TRACE) | low-medium | Dangerous methods advertised via OPTIONS, or TRACE echoes the request (XST). | Disable TRACE and unused write methods (PUT/DELETE) unless explicitly required. |
L3-011 |
Directory listing (autoindex) | medium | Conventional folders return an Apache/IIS/nginx auto-index of file names. | Disable autoindex; return 403/404 for bare directories. |
L3-003 |
Sensitive-file / cloud-config disclosure | high-critical | A fixed allowlist of paths (.env, .git, wp-config backups, /actuator/env, …) served with a sensitivity signature. | Deny dotfiles/backups at the web server; move config + secrets outside the web root. |
L3-012 |
Broken access control (missing auth) | high | Auth-vs-anon differential: a private-looking URL returns the same private content without a session. | Enforce server-side authorization on every sensitive route — never rely on hidden URLs. |
L3-013 |
IDOR / BOLA | high | With the owner's test session, a tampered object identifier returns a different valid object (cross-object access). | Check object ownership on every request; use unguessable IDs as defence-in-depth. |
L3-015 |
JWT weakness | high-critical | Offline analysis of supplied tokens: `alg:none`, weak/default HMAC secret, missing exp, sensitive claims. | Pin the algorithm; use a strong secret/asymmetric keys; set short exp; keep secrets out of the payload. |
L3-016 |
Weak session token | medium-high | Supplied session token has low entropy or base64-decodes to readable identity data (forgeable). | Use ≥128-bit random opaque session IDs; never encode identity into the token. |
L3-022 |
SSRF (out-of-band) | high | A unique callback URL pointing only at CamsClaw's collaborator; a received callback proves SSRF. Never targets internal/localhost. | Allowlist outbound destinations; block link-local/RFC1918; resolve+validate URLs server-side. |
Layer-3 active AI / LLM probing (consented)
A separate, token-cost-aware consent on top of active testing (it spends the customer's model tokens and hits their provider). All probes are benign — canaries, markers, and read-only listing, never real exploitation — single model-call each, hard spend-budget-capped. Mapped to OWASP LLM Top 10 (2025), OWASP Agentic-AI threats, MITRE ATLAS, NIST AI RMF.
| Test ID | Title | Severity | Description | Remediation |
|---|---|---|---|---|
L3-AI-001 |
System-prompt leakage (LLM07) | medium-high | Asks the model, via direct/indirect techniques, to reveal its system prompt. | Treat the system prompt as non-secret; don't place secrets in it; add output filtering. |
L3-AI-002 |
Direct prompt injection (LLM01) | medium-high | A canary instruction in user input; echoing the canary proves the model obeyed injected instructions. | Separate instructions from data; constrain tools; validate/escape model output downstream. |
L3-AI-007 |
Indirect / data-borne injection (LLM08) | medium-high | A canary instruction embedded in a 'retrieved document' the model is told only to summarise. | Sanitise/segment retrieved content; never let RAG context carry executable instructions. |
L3-AI-006 |
Sensitive information disclosure (LLM02) | high | Asks whether secrets/keys/connection-strings/internal hosts sit in context; matches are masked (raw never stored). | Keep secrets out of prompts/context; add output DLP; scope retrieval to the user. |
L3-AI-003 |
Insecure output handling (LLM05) | info-high | Boundary check — flags high only when the endpoint serves unescaped model output as text/html. | Treat model output as untrusted; encode before rendering; never eval/exec it. |
L3-AI-004 |
Excessive agency / exposed tools (LLM06) | medium-high | Enumerates an agent/MCP tool surface (listing only, never invokes a tool); flags dangerous capabilities. | Minimise tool scope; require human approval for high-impact tools; authenticate the agent surface. |
L3-AI-008 |
Unbounded consumption / cost controls (LLM10) | medium | One small output-capped call checks for rate-limit/cost-control headers (denial-of-wallet). Never floods. | Add per-user rate limits, token ceilings, and spend alerts on model endpoints. |
L3-AI-005 |
Guardrail / jailbreak resistance (LLM01) | medium | Opt-in only (highest provider-abuse risk): benign jailbreak payloads test refusal behaviour. | Layer input/output guardrails; don't rely on the base model's refusals alone. |
L3-AI-009 |
MCP tool poisoning (LLM06 / Agentic) | high | Injection phrases or hidden zero-width/Unicode-tag characters in MCP tool/prompt/resource metadata. | Sanitise tool metadata; reject invisible characters; review third-party MCP servers before connecting. |
L3-AI-010 |
MCP secrets in metadata (LLM06) | high | Secret-shaped values exposed in MCP tool/resource metadata (masked; raw never stored). | Keep credentials out of tool descriptions/schemas; inject them server-side at call time. |
L3-AI-011 |
MCP surface enumeration (LLM06) | low-medium | An exposed prompts/resources catalogue (recon value for an attacker mapping the agent). | Authenticate the MCP transport; don't expose listing methods to anonymous callers. |
L3-AI-012 |
MCP credential passthrough (LLM06) | medium | Tool input-schema parameters named like credentials (token/confused-deputy passthrough risk). | Don't accept credentials as tool params; broker auth server-side with least privilege. |
L3-AI-013 |
MCP invocation coercion (LLM06 / Agentic) | medium-high | Tool/prompt metadata that instructs the model to call a tool automatically, silently, or without confirmation — turning a poisoned description into unattended action (excessive agency). Detected statically from the catalogue; no tool is ever invoked. | Require human-in-the-loop for consequential tool calls; never let metadata dictate auto-invocation; pin and review tool definitions. |
L3-AI-014 |
Exfiltration-shaped MCP tool (LLM06 / Agentic) | medium | A tool schema that pairs a network-destination parameter (url/webhook/recipient) with a data-content parameter (content/history/context) — an injected agent could be coerced to send sensitive data to an attacker-chosen destination. Detected from the schema only. | Allowlist destinations server-side; separate data-egress tools from data-reading tools; require approval before a tool sends content off-system. |
L3-AI-015 |
Multi-turn / persisted prompt injection (LLM01) | medium-high | An instruction planted on an earlier conversation turn changes a later turn's behaviour (the model echoes a canary it was told to append two turns earlier). Conversational injection that single-turn defences miss — a poisoned history or injected first message steers every later reply. Benign canary, ~3 model calls, budget-gated. | Re-assert trusted system instructions each turn; sanitise/segregate stored history; treat every turn + retrieved content as untrusted; require human confirmation for agent actions. |
L3-AI-016 |
Model / endpoint fingerprint (recon) | info | Identifies the model family behind the endpoint from its declared id + a one-line self-identification reply (OpenAI/Anthropic/Google/Meta/Mistral/Qwen/…). Recon context and a pointer to family-specific known weaknesses. Informational — no weakness asserted. | Not a vulnerability itself; if undisclosed by design, don't echo the model id and instruct the model not to self-identify. Track advisories for the model family in use. |
What's not in this list
Three things on purpose:
- Layer-2 verified-owner checks (authenticated session crawls, post-login surface analysis, deeper subdomain enum) require ownership proof first — see /about.
- Layer-3 active probes (SQLi, XSS, SSRF, brute-force) require signed consent. Not enabled in public mode.
- Compliance frameworks themselves. Each finding above carries mappings to OWASP Top 10, PCI DSS 4.0, ISO 27001, NIST 800-53, GDPR, DPDPA, and the RBI Cyber Security Framework where applicable.
Re-running after a fix
Just run another scan — every scan is independent. The leaderboard pages show 24-hour deltas so improvements show up the next day. There's no cache to bust on our side.