Reduce false positives across all 15 checks (v2.1.0) - #5
Merged
Conversation
A deliberate false-positive audit (15 checks, adversarially verified)
found a systemic pattern: checks firing on a single signal without
confirming the premise. Confirmed FPs clustered into four root causes,
all addressed here.
1. No baseline comparison (active checks)
New HttpUtils.baselineContains(): the original rr.response() IS the
baseline, so no extra request is needed. A marker only counts as
evidence when it's present in the payload response AND absent from
the baseline.
- SsrfCheck: skip payloads whose marker is already in the baseline;
ignore 4xx / rejection responses (OAuth echoing a rejected
redirect_uri is not SSRF).
- InjectionCheck: SQL-error and command-output markers must be NEW
vs baseline (kills "/bin/bash in API docs", "syntax error on every
error page"). XSS now requires an HTML Content-Type — a JSON API
echoing the payload as a string value is not exploitable.
- AuthBypassTester: success markers must be new vs the baseline login
response and not co-occur with a failure marker. Removed broad
"logged"/"authenticated" markers that matched "not authenticated".
2. Status-only success (no body inspection)
New HttpUtils.looksRejected(): a 2xx carrying an error / "not found"
/ "forbidden" body is the server refusing access with a sloppy
status code, not success.
- BrokenObjectAuthCheck: ID-manipulation and enumeration now run
ONLY on authenticated requests — firing Critical on public
/articles/{id} endpoints was the worst FP in the audit. Plus
looksRejected guard on the success check.
- FunctionLevelAuthCheck: looksRejected guard on the privileged-
access check.
- MethodFuzzingCheck (TRACE): now sends a marker header and requires
it to be echoed back — XST is only real if TRACE reflects the
request. A canned 200 from a proxy no longer fires.
3. Keyword matches overclaiming (passive)
- ExcessiveDataExposure: benign-field exception list (content_hash,
etag, password_expiry_days, …); large-array finding suppressed
when pagination headers/params are present.
- BusinessFlow: removed the bare word "challenge" from anti-
automation markers (matched marketing copy and wrongly suppressed
real findings); kept specific CAPTCHA/Turnstile tokens.
- InventoryManagement: debug/management-endpoint finding downgraded
Certain -> Tentative (Low), reworded to note docs/health probes
are often intentional.
- UnsafeApiConsumption: webhook keyword set narrowed to
webhook-specific paths (dropped /callback, /notify, /event).
4. Threat-model blind spots
- BrokenAuth: API-key-over-HTTP skips loopback hosts (no
intermediary on localhost) via HttpUtils.isLoopbackHost().
- SecurityMisconfig (CORS reflected origin): downgraded High/Firm ->
Medium/Tentative and reworded — a single passive observation can't
distinguish validated allow-list reflection from blind reflection.
5. ParameterPollution: status-code change is now the primary (clean)
signal; the body-length-delta path measures the endpoint's natural
jitter (one unmodified re-request) and only fires when the pollution
delta clearly exceeds it — removes timestamp/UUID and verbose-error
FPs.
Docs: VALIDATION_GUIDE CERTAIN table corrected for baseline diffing and
the XSS/HTTP confidence changes; version bumped to 2.1.0 across pom,
banner, CI, and docs. Build verified clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A deliberate false-positive audit (all 15 checks, each claim adversarially verified) found a systemic pattern: checks firing on a single signal without confirming the premise. 29 confirmed FPs clustered into four root causes — fixed here.
Root causes addressed
1. No baseline comparison (active checks). New
HttpUtils.baselineContains()— the originalrr.response()is the baseline, so no extra request is needed. A marker only counts when present in the payload response AND absent from baseline.redirect_uri≠ SSRF)./bin/bash-in-docs,syntax error-on-every-error-page). XSS now requires HTML Content-Type.logged/authenticated.2. Status-only success. New
HttpUtils.looksRejected()— a 2xx with an error/"not found"/"forbidden" body isn't success./articles/{id}— the worst FP found).3. Keyword matches overclaiming (passive).
content_hash,etag,password_expiry_days); large-array suppressed when paginated.challengemarker (matched marketing copy)./callback,/notify,/event).4. Threat-model blind spots.
5. ParameterPollution: status-code change is the primary signal; body-length path now measures the endpoint's natural jitter and only fires when the pollution delta clearly exceeds it.
Diff
22 files, +417 / −79. Detection logic only; no architectural change. Build verified clean locally; CI runs on this PR.
Test plan
mvn clean packageclean.burp-api-scanner-2.1.0.jarin Burp; re-run the ginandjuice / brokencrystals scans and confirm the previously-flagged FPs (API-over-HTTP redirect, public-endpoint BOLA, etc.) no longer fire while genuine findings still do.Releases as v2.1.0 (behavioural change, not a patch).