strix/skills/analysis/fix_verification.md
When you attach fix_before / fix_after to a code location, you are not
writing advice. You are writing a suggestion block that a reviewer can
apply with one click, straight into their codebase. An unverified fix is
worse than no fix: it converts your uncertainty into their merged commit.
This skill covers what you must establish before that happens.
Never trade an earlier property for a later one. A smaller, tidier, more idiomatic patch that leaves the boundary open is a failure. Minimal means the smallest repository-native change that satisfies everything above it — not the fewest lines.
Establish these from the code, not from assumption:
Run these in order. A failure at any gate disqualifies the fix — revise the patch or withhold it. Do not compensate for a failed gate by making the diff smaller or the write-up longer.
1. Applicability. Read the final diff. Confirm it contains nothing
unrelated, that fix_before still matches the file character-for-
character, and that start_line/end_line still cover exactly those
lines. Run the narrowest syntax / import / type check available.
2. Security closure. Re-run the original PoC against the patched
code. If you cannot execute it, re-trace source → control → sink through
the patched source and state precisely which step now fails and why.
"The fix adds validation" is not closure; "the fix rejects ../ before
the path reaches open(), and open() is the only sink on this path" is.
3. Bypass review. Re-read the finding and the diff without leaning on the reasoning that produced the patch — you are looking for what that reasoning missed. Trace the changed branches from their direct callers. Check equivalent sinks and sibling call sites of the same helper. Try at least one alternate malicious input class: different encoding, different content type, a null byte, a unicode homoglyph, a nested/doubled payload, a different HTTP verb. A control that catches your one payload and nothing else has not closed the boundary.
4. Preserved behavior. Exercise the legitimate case through the same boundary. Confirm the APIs, error semantics, and compatibility constraints you recorded still hold. A fix that breaks the feature will be reverted, which means the vulnerability comes back.
5. Repository checks. Run the focused tests covering the changed lines, then the owning package's tests, then the applicable formatter, linter, and type checker. Use the repository's own commands.
Where practical, confirm the check would fail if the security change were removed. A test that passes both with and without the patch is proving nothing.
try/except that swallows
the failure, or returns "allowed" on error.If you cannot pass the gates, that is a legitimate outcome — say so
rather than shipping a guess. Drop fix_after from the location, leave
it informational, and put the remediation in prose in
remediation_steps instead. State in fix_verification exactly which
gate you could not clear and what was missing: the command that failed,
the service you could not start, the decision that needs a human.
Withhold and explain when:
Everything above goes in fix_verification, which is required whenever
any location carries a fix_after. Write the actual commands and their
results, grouped by gate, and mark every gate you could only reason
about — rather than execute — as an explicit gap. Do not hide proof
gaps; a reviewer who knows gate 5 was skipped can run it themselves, but
one who was told it passed cannot.