feat(validation): 13 more CWE validators, each with a rejection rule

Six classes had deterministic rules; the rest of a run still rested on models
voting. These thirteen cover the classes that produce the most false positives
in AI-driven testing, and each one is written around what *disproves* the
claim, because that is the part a language model skips:

  SSTI          an expression evaluated server-side whose result was never
                sent — the payload echoing its own "result" is rejected
  XXE           entity content or an OOB callback; a parser error mentioning
                entities shows the DTD was read, not that anything resolved
  Open redirect 3xx WITH a Location off-site; a rendered link is not a redirect
  CORS          reflected Origin PLUS credentials; ACAO:* without credentials
                exposes only what an anonymous client could already read, and
                ACAO:* WITH credentials is refused by browsers anyway
  Cookie flags  fully decidable from Set-Cookie + scheme
  Clickjacking  neither X-Frame-Options nor CSP frame-ancestors
  Auth bypass   protected content with NO credentials sent — a "bypass" whose
                request still carried a cookie is rejected, as is a redirect
                to login
  JWT           forged token accepted AND privileged content returned
  Rate limiting >= 20 attempts with no 429/Retry-After; five attempts prove
                nothing about a limit that was never reached
  Session fix.  the session id surviving login unchanged
  Mass assign.  a read-back proving the field persisted — a 200 on the write
                means nothing, APIs accept and ignore extra fields routinely
  CSRF          a cross-origin state change read back; a SameSite session
                cookie means a browser would never attach it cross-site
  Exposure      a real secret/listing signature the baseline lacked; a
                soft-404 mirroring the baseline page is rejected

Exchange gains response and request headers, because several of these classes
are decided by a header (Location, Set-Cookie, Access-Control-Allow-*) and the
body alone is not evidence for them.

A test asserts no two validators claim the same CWE — ambiguous ownership would
make routing depend on registration order, which is how a class silently gets
the wrong rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
CyberSecurityUP
2026-09-13 15:10:23 -03:00
co-authored by Claude Opus 5
parent 093c87fbc6
commit 6475dba752
2 changed files with 785 additions and 9 deletions
+24 -8
View File
@@ -324,14 +324,30 @@ HYPOTHESIS → CANDIDATE → [ VALIDATION ENGINE ] → CONFIRMED | NEEDS_REVIEW
Per-CWE rules, because "is this real?" has a different answer per class:
| class | what confirms it |
|-------|------------------|
| SQLi (89/943) | baseline vs attack difference **that reproduces ≥2×** |
| XSS (79/80) | a real browser executed a **harness-chosen marker** — reflection alone is not proof |
| IDOR/BOLA (639/862/863) | identity B reads identity A's resource **and the body matches** (a 200 returning a login page is rejected) |
| SSRF (918) | controlled callback, or retrieval of a canary resource |
| LFI (22/23/98) | controlled file marker, or a file signature the baseline lacked |
| RCE (77/78/94) | a unique nonce in command output or a callback — reflected input is rejected |
19 validators, each owning a disjoint set of CWEs (a test enforces that no two
claim the same one, so routing never depends on registration order):
| class | what confirms it | what it rejects |
|-------|------------------|-----------------|
| SQLi (89/943/564) | baseline↔attack difference **reproducing ≥2×** | an app that always prints SQL errors |
| XSS (79/80/83/87) | a browser executed a **harness-chosen marker** | reflection in HTML |
| IDOR/BOLA (639/862/863/284/285) | identity B reads A's resource **and the body matches** | a 200 that is really a login page; a 403 |
| SSRF (918) | controlled callback or canary retrieval | timing alone |
| LFI (22/23/35/98/73) | controlled marker or a file signature the baseline lacked | a signature the baseline already had |
| RCE (77/78/94/95/502/917) | unique nonce in output, or a callback | a nonce that is only reflected input |
| SSTI (1336) | an expression evaluated server-side whose **result was never sent** | the payload echoing its own "result" |
| XXE (611/776/827) | entity content returned, or an OOB callback | a parser error mentioning entities |
| Open redirect (601) | 3xx **with** a `Location` pointing off-site | a rendered link; a same-origin redirect |
| CORS (942/346/1385) | reflected `Origin` **plus** credentials | `ACAO: *` without credentials (browsers already refuse it) |
| Cookie flags (614/1004/1275) | decided entirely by `Set-Cookie` + scheme | a cookie that carries all three flags |
| Clickjacking (1021) | neither `X-Frame-Options` nor CSP `frame-ancestors` | either control present |
| Auth bypass (306/287/288) | protected content served with **no credentials sent** | a "bypass" that still carried a cookie; a login redirect |
| JWT (347/345/290) | forged token accepted **and** privileged content returned | a 401 on the forged token |
| Rate limiting (307/799/770) | ≥20 attempts, none throttled | any 429 / `Retry-After` in the burst |
| Session fixation (384) | the session id survives login unchanged | a regenerated id |
| Mass assignment (915/913) | a read-back showing the privileged field persisted | a 200 on the write alone (APIs accept and ignore extras) |
| CSRF (352) | cross-origin state change **read back** | a GET; a 403; a `SameSite` session cookie |
| Exposure (200/538/540/548/312/532) | a real secret/listing signature the baseline lacked | a soft-404 that mirrors the baseline page |
Two rules keep it honest: absent evidence is **never** a pass (it becomes
`needs-review`), and a class with no rule is never auto-confirmed.