Skip to content

fix(review): close four residual gaps in the injection-sink floor's defeaters - #701

Merged
devops-thiago merged 7 commits into
mainfrom
fix/696-floor-defeater-residuals
Aug 14, 2026
Merged

fix(review): close four residual gaps in the injection-sink floor's defeaters#701
devops-thiago merged 7 commits into
mainfrom
fix/696-floor-defeater-residuals

Conversation

@devops-thiago

@devops-thiago devops-thiago commented Aug 13, 2026

Copy link
Copy Markdown
Owner

What type of PR is this?

  • 🐛 Bug fix

Description

Closes the four residual gaps in the injection-sink floor's defeater patterns in FindingVerificationService that survived the #676/#685 hardening:

  1. parameteriz added to DEFENSE_OBJECT_AFTER_VERB's defense stems — "Nothing escapes parameterization before the query runs" is the mitigation it asserts and no longer over-fires the floor.
  2. Modal absence claims register — a new ABSENCE_MODAL_VERB token reads "Nothing can sanitize the value" as the absence claim it is (one non-negator adverb admitted, sharing the finite wording's subject prefix), with the same defense-noun-object rejection so "nothing can escape validation" stays a mitigation.
  3. MITIGATION_DO_SUPPORTED admits one non-negator gap word — "the framework does always escape the value" now defeats the floor, while "does not escape" stays an absence claim via the negator lookahead.
  4. Punctuation separators, decided deliberately: widened for clause breaks onlyDEFENSE_OBJECT_AFTER_VERB's separator accepts clause-break punctuation (; : and dashes), so "Nothing escapes; the sanitizer runs on render" reads as mitigated. The comma and sentence-ending punctuation are deliberately excluded: comma-coordinated absence verbs ("Nothing escapes, sanitizes, or validates the value") and appositive denials ("Nothing escapes, but the sanitizer is disabled") stay absence claims, and a defense noun in the NEXT sentence cannot defuse this sentence's claim — all tested. The denied defense across a clause break is documented in the javadoc as accepted residual under-fire. As part of the coordination case, MITIGATION_ASSERTED_SUBJECT excludes coordinators from its subject slot so "or validates" is not read as a subject-verb mitigation.

Over-fire was the dangerous direction; items 1, 3, 4 stop flooring mitigated findings, and item 2's under-fire fix carries its own over-fire guard.

Related Issues

Closes #696

How Has This Been Tested?

  • Unit tests

Eleven new tests in FindingVerificationServiceTest cover every acceptance criterion plus the over-fire guards (modal defense-noun object, negated do-support, next-sentence defense noun). Full ./mvnw verify clean locally.

Checklist

  • My code follows the project's coding standards
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly
  • My changes generate no new warnings or errors

Screenshots / Logs

N/A

Additional Notes

Each pattern's javadoc is updated to stay accurate, and the CHANGELOG gains an [Unreleased] Fixed entry. Depends on #703: this branch is rebased onto refactor/sonar-new-code-smells and expresses the semantic changes in that PR's split pattern shapes (which is also what keeps each pattern under the S5843 complexity budget). Kept as draft until #703 merges; it will then be rebased onto main.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@thrillhousebot

Copy link
Copy Markdown
Contributor

⚠️ Severity did not decide this verdict. 1 finding(s) were severe enough to block on their own, but the review hedged its confidence in them, so this review comments instead of requesting changes. A hedge means the finding could not be confirmed from the diff alone — not that it was disproven — so read those findings before merging. Set REVIEW_BLOCKING_STRICTNESS=strict to let severity alone decide.

🤖 ThrillhouseBot PR Summary

What this PR does

Tightens the injection-sink floor's defeater heuristics in FindingVerificationService: 'parameteriz' joins the defense-noun lookahead, the defense-noun separator admits weak punctuation, a new MITIGATION_ABSENT_MODAL pattern registers modal absence claims, and MITIGATION_DO_SUPPORTED admits one non-negator gap word; seven new tests cover each change and the over-fire guards.

Description vs. Implementation

No mismatch found between the PR description and the change.

Changes Overview

  • Files changed: 3
  • Lines added: +217
  • Lines removed: -14

Changed Files

File Change Summary
CHANGELOG.md Modified Adds an [Unreleased] Fixed entry for the four residual defeater gaps (#696).
src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java Modified Widens the floor's defeater heuristics: parameteriz in the defense-noun lookahead, weak-punctuation separators, new MITIGATION_ABSENT_MODAL pattern, and a one-word non-negator gap in MITIGATION_DO_SUPPORTED.
src/test/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationServiceTest.java Modified Adds seven tests: parameterization mitigation, modal absence (with and without defense-noun object), do-support adverb gap and negated gap, punctuation-separated defense noun, and next-sentence boundary.

Risk Assessment

Risk Count
🔴 Critical 0
🟠 High 1
🟡 Medium 0
🔵 Low 1

Key Findings

  • HIGH: Modal absence pattern misses adverbs and 3-word subject gaps that sibling patterns accept (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:196)

Things to double-check

1 lower-confidence finding
  • LOW: Widened punctuation separator flips absence claims whose defense noun is itself denied (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:179) (low confidence — verify before acting)

⚠️ Required CI Checks Status

Some required checks are still pending or have failed:

Check Type Status Detail
format check-run ⏳ Pending -
test check-run ⏳ Pending -
frontend check-run ⏳ Pending -
trivy check-run ⏳ Pending -
dependency-review check-run ⏳ Pending -

Automated review by ThrillhouseBot. Reply with /review to re-run.

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot noted 1 lower-confidence item(s) under Things to double-check in the PR summary (not posted as inline threads):

  • LOW: Widened punctuation separator flips absence claims whose defense noun is itself denied (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:179)
    The new separator class in MITIGATION_ABSENT_SUBJECT's negative lookahead ([\s;:,\u2013\u2014-]+) makes any defense stem within about two words of the escaping verb flip the absence claim into a mitigation, even when the defense mention is part of the denial. Input not in the diff: "Nothing escapes, but the sanitizer is disabled in this diff." — after "escapes", ", " is now a separator, "but " and "the " fill the two word groups, and "sanitiz" matches "sanitizer", so the lookahead's inner pattern succeeds, the negative lookahead fails, and the absence claim is not registered: the floor stays silent although the text explicitly says the sanitizer is NOT running. The same flip occurs for a list of denied defenses: "Nothing escapes, sanitizes, or validates the value" — the continuation verbs "sanitizes"/"validates" are part of the denial, yet the second/third defense stems flip the claim. Under the previous whitespace-only separator, the comma blocked the lookahead and both inputs floored, so this is a sensitivity regression on the same class the PR's own new test treats as absence ("Nothing escapes. The sanitizer was removed in this diff." floors). Verify by running these sentences through service.verify() with a sink-named finding — if they are expected to floor, the lookahead needs to distinguish a denied/disabled defense mention (e.g. restrict the separator or the word window to noun-object slots), or this residual should be documented the way the sentence-boundary case is.

@thrillhousebot thrillhousebot Bot added bug Something isn't working java Pull requests that update java code security Security-sensitive issue or hardening labels Aug 13, 2026
@devops-thiago
devops-thiago marked this pull request as draft August 13, 2026 19:05
@devops-thiago
devops-thiago force-pushed the fix/696-floor-defeater-residuals branch 2 times, most recently from 9b4d9c0 to 296ba61 Compare August 13, 2026 19:06
@devops-thiago

Copy link
Copy Markdown
Owner Author

@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:179 — Widened punctuation separator flips absence claims whose defense noun is itself denied

Both quoted regressions are fixed: the comma is removed from DEFENSE_OBJECT_AFTER_VERB's separator class, so "Nothing escapes, but the sanitizer is disabled in this diff" and "Nothing escapes, sanitizes, or validates the value" both floor again (tests floorsWhenTheDefenseAcrossTheCommaIsDenied, floorsWhenTheAbsenceVerbsAreCoordinatedByCommas; the coordination case also needed MITIGATION_ASSERTED_SUBJECT to exclude coordinators from its subject slot). The remaining case — a DENIED defense across a semicolon/colon/dash clause break ("Nothing escapes; the sanitizer is disabled") — is documented in the pattern's javadoc as accepted residual under-fire, the safe direction, since a regex cannot carry the denial back over the punctuation.

🤖 Addressed by Claude Code

@thrillhousebot

Copy link
Copy Markdown
Contributor

The next review will close every previous finding this comment names by its path:line and title; anything it does not name stays open.

@devops-thiago
devops-thiago force-pushed the fix/696-floor-defeater-residuals branch 2 times, most recently from 060dca8 to ee78bfe Compare August 13, 2026 20:09
@devops-thiago
devops-thiago marked this pull request as ready for review August 13, 2026 20:53
@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 0
  • Previous findings resolved: 2
  • Previous findings still open: 0

@thrillhousebot

Copy link
Copy Markdown
Contributor

⚠️ ThrillhouseBot review could not be completed.

The review service encountered an error. Please reply with /review or @Thrillhousebot review to retry.

@devops-thiago

Copy link
Copy Markdown
Owner Author

/review

@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 2
  • Previous findings resolved: 2
  • Previous findings still open: 0

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot noted 1 lower-confidence item(s) under Things to double-check in the PR summary (not posted as inline threads):

  • MEDIUM: Comma-coordinated asserted mitigation keeps over-firing (undocumented comma-exclusion residual) (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:216)
    The comma exclusion in DEFENSE_OBJECT_AFTER_VERB's separator class is meant to keep appositive denials ("Nothing escapes, but the sanitizer is disabled") as absence claims — correct and tested. But it also prevents a comma-coordinated ASSERTED mitigation from flipping the reading. Input not in the diff: "Nothing escapes, but the sanitizer runs on render." The verb token "escapes" has the "Nothing " prefix, the defense-object check fails on the leading comma (line 216: "[\s;:\u2013\u2014-]+..." cannot match ","), and no mitigation pattern matches "sanitizer runs" (the subject-slot verb list is sanitizes/escapes/validates/parameterizes/encodes and the clause has no be/get/do-support auxiliary) — so the floor fires at high on a sentence that explicitly asserts the sanitizer runs on render. Compare the PR's own test doesNotFloorWhenPunctuationSeparatesTheDefenseNounObject, where the semicolon twin "Nothing escapes; the sanitizer runs on render" does NOT floor: only the punctuation differs. The javadoc above this pattern documents the denial and coordination rationale ("a comma carries coordination ... and appositive denials ...") but not this asserted-follow-up residual, which is exactly the over-fire direction the PR calls dangerous — while the mirror residual (denied defense across a semicolon) IS documented as accepted. The clause splitter treats "but" as a real boundary, so this is a same-sentence asserted mitigation that gets floored. Verify by running "Nothing escapes, but the sanitizer runs on render." through service.verify() with a sink-named finding; if it floors as traced, handle or document the comma-asserted case.

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new issues in this revision, but 2 previous finding(s) remain unresolved — fix them, or reply on their review thread with why they are deferred. A finding listed only under "Things to double-check" has no thread: clear it by commenting @thrillhousebot resolved path/to/File.java:42 — <the finding's title> on this PR.

@devops-thiago
devops-thiago force-pushed the fix/696-floor-defeater-residuals branch from d4389d2 to cf0ed38 Compare August 13, 2026 21:23
@devops-thiago

Copy link
Copy Markdown
Owner Author

@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:216 — Comma-coordinated asserted mitigation keeps over-firing (undocumented comma-exclusion residual)

Fixed rather than documented: a new MITIGATION_DEFENSE_ACTION pattern reads a defense-stemmed subject with an operate-family verb directly after it ("the sanitizer runs on render"), so "Nothing escapes, but the sanitizer runs on render" now defeats the floor via assertsMitigation while the comma exclusion keeps the absence claim registered for the denial cases. A NEGATING_DETERMINER guard keeps "no sanitizer runs" an absence statement, and the direct-adjacency requirement keeps "the sanitizer never runs" one too. Tests: doesNotFloorWhenTheCommaCoordinatedFollowUpAssertsTheDefenseRuns, floorsWhenNoSanitizerRuns, floorsWhenTheSanitizerNeverRuns; the DEFENSE_OBJECT_AFTER_VERB javadoc now documents the comma-asserted case alongside the denial rationale.

🤖 Addressed by Claude Code

@thrillhousebot

Copy link
Copy Markdown
Contributor

The next review will close every previous finding this comment names by its path:line and title; anything it does not name stays open.

@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 2
  • Previous findings resolved: 2
  • Previous findings still open: 0

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot noted 2 lower-confidence item(s) under Things to double-check in the PR summary (not posted as inline threads):

  • MEDIUM: Coordinator exclusion makes conjoined-verb mitigations over-fire the floor (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:325)
    Input not in the diff: "Nothing escapes, but the framework renders the output and escapes it at render." The new MITIGATION_ASSERTED_SUBJECT excludes the coordinators and/or/nor from its subject slot, so the genuine mitigation pair "and escapes" can no longer match (the token before "escapes" is the excluded "and"), and no other subject-slot pair exists because "renders" is not in the verb list. The removed one-pattern alternative did match this sentence via the unexcluded "and" subject, so this is a new over-fire regression in exactly the direction the PR calls dangerous. Tracing the new code: the "escapes" token registers the absence claim (the comma is deliberately not a DEFENSE_OBJECT_AFTER_VERB separator), assertsMitigation() finds nothing (BE/GET none, do-support none, MITIGATION_DEFENSE_ACTION requires a defense-stem subject directly before an operate verb — "framework renders" and "escapes it" are neither), and the floor raises the finding to high on a sentence that asserts the framework escapes at render. The javadoc's own example ("the framework validates and escapes the value" still matches on "framework validates") only holds when the first conjoined verb is defense-listed; a verb-phrase coordination whose first verb is not in the list loses its only subject-verb match. Verify by running the sentence through service.verify() with a sink-named finding; a fix needs the subject-slot walk to step past a coordinator to the real noun phrase, which the current one-pattern shape cannot express. The in-diff tests cover the absence-coordination side ("Nothing escapes, sanitizes, or validates") but not this asserted-mitigation side.
  • MEDIUM: Defense-action verb must directly follow subject, so adverb-modified mitigations over-fire (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:360)
    Input not in the diff: "Nothing escapes, but the sanitizer always runs on render." MITIGATION_DEFENSE_ACTION requires the operate-family verb immediately after the defense-stem subject, so "sanitizer always runs" does not match ("always" intervenes), the subject-slot and do-support patterns do not match either, while the comma keeps "Nothing escapes" registered as an absence claim — and the floor raises to high on a sentence that asserts the defense operates on render. This is the mirror of the do-support adverb case this PR fixed: MITIGATION_DO_SUPPORTED admits one gap word with a negator lookahead so "does always escape" defeats the floor while "does not escape" does not. The defense-action rule instead forbids the gap entirely, so positive adverbs ("always runs", "consistently runs") are invisible along with the intended "never runs". The javadoc documents the "never" side of the decision but not this over-fire cost on the asserted side. Verify with service.verify(); if it floors as traced, admit one non-negator gap word here the same way MITIGATION_DO_SUPPORTED does — "never runs" would then be handled by the negator lookahead plus NEGATING_DETERMINER instead of by absence of a match. The in-diff tests cover the unmodified "sanitizer runs" form but not the adverb-modified assertion.

devops-thiago added a commit that referenced this pull request Aug 13, 2026
… refused reviews as comments (#705)

## What type of PR is this?

- [x] 🐛 Bug fix

## Description

A review on #701 was lost to a GitHub 422 that the logs could not
explain. This PR ships the three fixes from the postmortem:

1. **The 422 body is logged.** A rejected review post now reads the HTTP
response off the failure's cause chain through the existing
`GitHubApiError` seam and logs its diagnostics line — status, throttle
headers, and the response body, with credential-shaped text redacted and
the length capped. The next 422 will name its cause.
2. **A superseded run abandons its post.** Just before the first write,
`ReviewOrchestrator` re-reads the PR head (a fresh read through the
token-healing read seam), and when it moved from the sha the run
reviewed, the post is abandoned: counted as a structured `HEAD_MOVED`
skip via `ReviewSkipEmitter`, the stale check run concluded as
`skipped`, and nothing user-facing posted — the dispatcher's coalesced
run for the new head re-reviews and posts in its place. This also stops
inline comments resolving against a diff that moved underneath them.
Fail-open: a failed head read never abandons a finished review.
3. **A refused summary-only review falls back to an issue comment.**
`createReviewWithFallback` keeps today's retry-without-comments path;
when a no-comments review (or that retry) is refused, the same body is
posted as an issue comment — through the capped, paced
conversation-write path — with a note that GitHub refused the review
post, instead of discarding the generation behind "review could not be
completed". `ReviewPostException` now fires only when the comment
fallback fails too.

Per the issue, this does **not** attempt to fix the unknown 422 root
cause (it cannot be named until fix 1 ships) and does not change
review-event semantics beyond what the fallback needs.

## Related Issues

Closes #704

## How Has This Been Tested?

- [x] Unit tests
- [ ] Integration tests
- [ ] Manual testing

New tests cover: the rejection-diagnostics path with a response-carrying
cause chain; the no-comments 422 falling back to an issue comment
carrying the body (and the zero-issues message when the body is
blank/null); the comment-carrying 422 keeping the retry-without-comments
path and falling through to the comment fallback when the retry is
refused too; `ReviewPostException` (with the comment failure suppressed)
when the fallback also fails; the full-path head-moved abandon (no
review, no comment, `HEAD_MOVED` counted, check run `skipped`);
`headMoved` comparisons including fail-open; abandon surviving check-run
and persistence failures; and `currentHeadSha` returning the fresh head
and empty on every unreadable shape. `./mvnw verify` passes (3222 tests)
and a local merge-base JaCoCo simulation of codecov/patch reports 100%
on changed lines.

## Checklist

- [x] My code follows the project's coding standards
- [x] I have performed a self-review of my own code
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I have updated the documentation accordingly
- [x] My changes generate no new warnings or errors

## Screenshots / Logs

N/A

## Additional Notes

CHANGELOG updated under `[Unreleased]` → Fixed.
@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 1
  • Previous findings resolved: 2
  • Previous findings still open: 0

…efeaters

Rebased onto the #703 split shapes. DEFENSE_OBJECT_AFTER_VERB gains the
parameteriz stem the verb group already had and accepts clause-break
punctuation (semicolon, colon, dashes) as a separator, so "Nothing
escapes parameterization" and "Nothing escapes; the sanitizer runs on
render" read as the mitigations they assert instead of over-firing the
floor; the comma is deliberately excluded so comma-coordinated absence
verbs and appositive denials stay absence claims, and the denied
defense across a clause break is documented as accepted residual
under-fire. A new ABSENCE_MODAL_VERB token registers modal absence
claims ("Nothing can sanitize the value", one non-negator adverb
admitted), MITIGATION_DO_SUPPORTED admits one non-negator gap word so
"does always escape" defeats the floor while "does not escape" stays an
absence claim, and MITIGATION_ASSERTED_SUBJECT excludes coordinators
from its subject slot so "Nothing escapes, sanitizes, or validates the
value" does not read its own continuation as a mitigation.

Closes #696
…tigation it asserts

MITIGATION_DEFENSE_ACTION reads "the sanitizer runs on render" — no
auxiliary, verb outside the subject-slot list — so the comma exclusion
that keeps appositive denials flooring no longer over-fires the floor
on an asserted follow-up; a NEGATING_DETERMINER guard keeps "no
sanitizer runs" and "the sanitizer never runs" as the absence
statements they are. The end-anchored negation checks now scan a
bounded whitespace-aligned Matcher.region window instead of copying
the whole prefix per token, keeping the defeater walks linear in the
finding text.
…nse actions defeating the floor

The coordinator exclusion on MITIGATION_ASSERTED_SUBJECT dropped the
only subject-verb match a conjoined pair has when its first verb is not
defense-listed ("renders the output and escapes it"), over-firing the
floor. Coordinators stay in the subject slot; a coordinator match is
dropped only when an ABSENCE_VERB_CHAIN closes the text before it, so
"Nothing escapes, sanitizes, or validates the value" still floors.
MITIGATION_DEFENSE_ACTION admits one non-negator gap word, mirroring
the do-support adverb gap, so "the sanitizer always runs" defeats the
floor while "never runs" and "no sanitizer runs" stay absence
statements.
@devops-thiago
devops-thiago force-pushed the fix/696-floor-defeater-residuals branch from 748f906 to 4d9ee16 Compare August 13, 2026 23:56
@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 1
  • Previous findings resolved: 1
  • Previous findings still open: 0

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot noted 1 lower-confidence item(s) under Things to double-check in the PR summary (not posted as inline threads):

  • LOW: NEGATING_DETERMINER's two-word bound denies idiomatic "no doubt the sanitizer runs" (over-fire) (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:409)
    NEGATING_DETERMINER \b(no|not|never|without|nor)\s+(\w+\s+){0,2}\z turns any negator within two words of a MITIGATION_DEFENSE_ACTION match into a denial. Input not in the diff: "Nothing escapes, no doubt the sanitizer runs on render." The action pattern matches "sanitizer runs"; the prefix "no doubt the " satisfies negator + two words + \z, so the denial fires, no other mitigation pattern matches the sentence, and the absence claim ("escapes" — comma excluded from DEFENSE_OBJECT_AFTER_VERB) stands — the floor fires HIGH on a sentence that emphatically asserts the sanitizer runs. The floor also fired before this change (no defense-action pattern existed), so this is a missed improvement rather than a regression — but the new rule contradicts its own documented purpose ("the clause then states the defense does NOT operate"), and over-fire is the direction the PR declares dangerous. This is the false-denial cost the previous review anticipated when the bound was widened from one to two words. Verify the intended handling with service.verify() on the synthesized input; if "no doubt"-type idioms are out of scope, document the trade-off in the javadoc.

… regex budget

ABSENCE_CHAIN_LINK is walked back link by link to the negated subject,
and the defense-action pair is decided by an end-anchored subject
prefix whose captured gap word carries the negator exclusion in code,
so both recognizers accept exactly what the one-pattern forms accepted
while each pattern stays under the S5843 complexity budget. The
negating determiner admits two modifier words ("not a single sanitizer
runs") and documents the idiomatic-negator residual.
@devops-thiago

Copy link
Copy Markdown
Owner Author

@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:325 — Coordinator exclusion makes conjoined-verb mitigations over-fire the floor
@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:360 — Defense-action verb must directly follow subject, so adverb-modified mitigations over-fire
@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:409 — NEGATING_DETERMINER's two-word bound denies idiomatic "no doubt the sanitizer runs" (over-fire)

All three are addressed on the current head:

  • Conjoined-verb mitigations: the coordinator exclusion is reverted — coordinators stay in MITIGATION_ASSERTED_SUBJECT's subject slot, so "the framework renders the output and escapes it" keeps its only subject-verb match. A coordinator match is dropped only when continuesAbsenceChain walks ABSENCE_CHAIN_LINKs back to a negated subject, so "Nothing escapes, sanitizes, or validates the value" still floors. Tests: doesNotFloorWhenAConjoinedVerbPairAssertsTheMitigation, floorsWhenTheAbsenceVerbsAreCoordinatedByCommas.
  • Adverb-modified defense actions: the defense-action pair admits one non-negator gap word (captured and checked in hasUndeniedDefenseAction), mirroring the do-support adverb gap, so "the sanitizer always runs" defeats the floor while "never runs" and "no sanitizer runs" stay absence statements. Tests: doesNotFloorWhenTheDefenseActionCarriesAnAdverb, floorsWhenTheSanitizerNeverRuns, floorsWhenNoSanitizerRuns, floorsWhenNotASingleSanitizerRuns.
  • Idiomatic negators: documented as accepted residual in NEGATING_DETERMINER's javadoc, per the finding's own alternative — "no doubt the sanitizer runs" floored before this PR too (no defense-action reading existed), so it is a missed improvement, not a regression, and exempting idioms is an open-ended lexicon problem a bounded regex cannot close.

🤖 Addressed by Claude Code

@thrillhousebot

Copy link
Copy Markdown
Contributor

The next review will close every previous finding this comment names by its path:line and title; anything it does not name stays open.

@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 2
  • Previous findings resolved: 1
  • Previous findings still open: 0

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot noted 2 lower-confidence item(s) under Things to double-check in the PR summary (not posted as inline threads):

  • LOW: Chain walk attributes a real-subject verb to 'Nothing' and can drop an asserted mitigation (over-fire) (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:911)
    New rule: continuesAbsenceChain (with ABSENCE_CHAIN_LINK = \b(sanitiz|escap|validat|parameteriz|encod)\w*[,\s]+\z and the confirmation if (negated.region(0, at).find())) drops a coordinator-subject mitigation match when a defense-verb link before the coordinator sits within the NEGATING_SUBJECT_BEFORE_VERB window of any negating pronoun — it never checks that the link verb's OWN subject is that pronoun. Input not in the diff: "Nothing escapes, the framework sanitizes, or validates the value before innerHTML receives it." The sentence asserts "the framework sanitizes or validates the value" (a mitigation); "Nothing escapes" is a separate clause. The absence claim registers: "escapes" is followed by a comma, deliberately excluded from DEFENSE_OBJECT_AFTER_VERB's separator, so no flip. The subject-slot match "framework sanitizes" is dropped by NEGATING_SUBJECT's prefix check ("Nothing" two words back, within the documented {0,3} word room). The new chain check then drops "or validates": the link matches "sanitizes, " and the negation confirmation at that link's start sees "Nothing escapes, the framework " — "Nothing" three words back — although "sanitizes" belongs to the real subject "the framework". No do-support and no defense-action match, so assertsMitigation returns false and the floor fires HIGH on an asserted mitigation — the over-fire direction the PR's javadocs call dangerous — and the ABSENCE_CHAIN_LINK javadoc's claim "so a real subject earlier in the sentence is never chained away" does not hold for this shape. Verify with service.verify() on the synthesized input; the decisive unknown is the exact word window of the pre-existing NEGATING_SUBJECT pattern (not fully shown in the diff) that drops "framework sanitizes" — if that window is narrower than two words, the earlier subject-verb match survives and itself defeats the floor.
  • LOW: Coordinated absence claim carrying a shared object is not recognized; 'or validates' reads as mitigation (under-fire) (src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:346)
    New rule: ABSENCE_CHAIN_LINK (\b(sanitiz|escap|validat|parameteriz|encod)\w*[,\s]+\z) only matches when the run straight before the coordinator ends in a defense-verb word plus comma/space. A coordinated absence claim that carries a direct object before its last coordinator escapes the drop: input not in the diff — "Nothing escapes, sanitizes the value, or validates the value before innerHTML receives it." The text before "or" ends "... the value, "; "value" is not a defense stem, the link cannot match, the walk returns false, and "or validates" is kept as a subject-slot mitigation. The absence verbs "escapes"/"sanitizes" register (the comma blocks the defense-object flip), yet a mitigation is also asserted, so the floor stays silent on a sentence asserting none of escaping, sanitizing or validating happens — the same claim class the PR's item-4 coordination test covers ("Nothing escapes, sanitizes, or validates the value"). Under-fire is the PR's documented safe direction, so this is a missed improvement rather than a regression, but item 4's acceptance criterion is only met for object-less verb chains. Verify with service.verify() on the synthesized input.

@devops-thiago

Copy link
Copy Markdown
Owner Author

@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:911 — Chain walk attributes a real-subject verb to 'Nothing' and can drop an asserted mitigation (over-fire)
@thrillhousebot resolved src/main/java/dev/thiagogonzaga/thrillhousebot/review/ai/FindingVerificationService.java:346 — Coordinated absence claim carrying a shared object is not recognized; 'or validates' reads as mitigation (under-fire)

Item 1 — declined as a false positive, now pinned by a test. The trace does not hold on two independent counts: (a) NEGATING_SUBJECT_BEFORE_VERB's gap is (\w+[\s-]+){0,3} — a gap word must be followed by whitespace or a hyphen, so the gap cannot cross the comma in "escapes," and the confirmation at the "sanitizes" link sees no negating pronoun; the walk then needs a defense-stem link before "sanitizes", finds "framework", and returns false, keeping "or validates". (b) Independently, "framework sanitizes" is NOT dropped: NEGATING_SUBJECT is \b(nothing|nobody)\s*$ — the pronoun must sit directly before the match, and "Nothing escapes, the " does not end with it, so that subject-slot pair alone already defeats the floor. New test doesNotFloorWhenARealSubjectInterruptsTheChain runs the exact probe sentence and asserts no floor.

Item 2 — accepted residual, documented. "Nothing escapes, sanitizes the value, or validates the value" leaves the trailing "or validates" reading as a mitigation because the object breaks the link run — under-fire, the documented safe direction; the absence claim is still registered by the chain's earlier verbs, and carrying objects through the links is an open-ended grammar problem. Now documented in ABSENCE_CHAIN_LINK's javadoc alongside the other residuals.

Per the stop-rule discussed on this PR's earlier rounds: further phrasing variants of the documented residuals (comma-asserted denials across clause breaks, idiomatic negators, object-carrying chains) will be declined citing the javadoc.

🤖 Addressed by Claude Code

@thrillhousebot

Copy link
Copy Markdown
Contributor

The next review will close every previous finding this comment names by its path:line and title; anything it does not name stays open.

@sonarqubecloud

Copy link
Copy Markdown

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrillhouseBot found no issues in this PR, but some checks are still pending or failed:

  • Check test is pending

Additionally, No new issues in this revision, but 2 previous finding(s) remain unresolved — fix them, or reply on their review thread with why they are deferred. A finding listed only under "Things to double-check" has no thread: clear it by commenting @thrillhousebot resolved path/to/File.java:42 — <the finding's title> on this PR.

@devops-thiago

Copy link
Copy Markdown
Owner Author

@thrillhousebot review

@thrillhousebot

Copy link
Copy Markdown
Contributor

🤖 ThrillhouseBot — changes since the last review

  • New findings this round: 0
  • Previous findings resolved: 1
  • Previous findings still open: 0

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything's coming up Thrillhouse! 🎉

No issues found in this PR.

@thrillhousebot thrillhousebot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything's coming up Thrillhouse! 🎉

No issues found in this PR.

@devops-thiago
devops-thiago merged commit 176371a into main Aug 14, 2026
17 checks passed
@devops-thiago
devops-thiago deleted the fix/696-floor-defeater-residuals branch August 14, 2026 02:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working java Pull requests that update java code security Security-sensitive issue or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(review): close four residual gaps in the injection-sink floor's defeaters

1 participant