Propose a minimal code patch that fixes a confirmed finding. You are not shipping the fix; you are handing the analyst a starting diff and explaining what it does. The analyst reviews, edits if needed, and opens a PR by hand.
-
Read ./context.json. If scrutineer.finding_id is missing, write {"error": "no finding_id in context.json; patch is finding-scoped"} to report.json and exit 0.
-
Fetch the finding: GET {api_base}/findings/{finding_id} with Authorization: Bearer {token}. Read location, cwe, trace, boundary, validation, rating. These five together tell you where the sink is, what the vulnerable input flow looks like, and what dangerous behaviour you need to stop.
-
Inside ./src, edit files to fix the finding. Constraints:
- Minimal. Change only what the fix requires. Do not refactor surrounding code, rename variables, reformat unrelated lines, or upgrade dependencies unless the fix inherently requires it.
- In place. Fix the sink where it lives. If the finding's
location is pkg/foo/bar.go:42, that is where the patch should land (or at the nearest layer where a guard is sensible — e.g. the input validator that feeds the sink).
- Consistent. Match the existing code style and idioms. If the codebase uses a specific sanitiser, validator, or helper for similar cases, reuse it. Do not introduce a new helper module for a one-off fix.
- Safe. The patch must not break the reproduction's documented legitimate behaviour — only block the dangerous path. If you cannot tell where the dangerous path diverges from legitimate use, stop and refuse to patch (see "Refusing to patch" below for the
{"error": ...} shape).
- Include a test when practical. If the repo has a test suite that covers the vulnerable code path, add a regression test that would fail without your patch. If the repo has no tests, or the sink is in a place that is hard to cover, skip this and say why in
rationale.
-
Once you have a working tree edit, generate a unified diff against HEAD:
cd src
git add -N .
git diff HEAD -- . > ../patch.diff
Read ../patch.diff (the workspace root, alongside report.json) and put its contents into report.json under the patch field. Do not commit; the diff is the artefact. If the diff is empty, something went wrong — do not write an empty patch. Write {"error": "patch produced no diff"} and exit 0.
-
POST a finding note summarising the patch: POST {api_base}/findings/{finding_id}/notes with:
{
"body": "Proposed patch in scan #{scan_id}.\n\nFiles changed: ...\n\n{short rationale}\n\nApply with: `git apply` the diff from the scan report.",
"by": "patch"
}
The note lives on the finding page; the full diff lives in report.json and is viewable on the scan page.
-
Do not PATCH any editable fields on the finding. Specifically:
- Do not set
fix_commit — that field means a shipped upstream fix, not a proposal. The analyst sets it after their PR merges.
- Do not set
fix_version — same reason.
- Do not touch
status. Lifecycle transitions belong to the analyst.
-
Write ./report.json:
{
"patch": "diff --git a/pkg/foo/bar.go b/pkg/foo/bar.go\n...",
"rationale": "Short prose — two or three sentences. What the guard is, why it blocks the trace, what legitimate input it still lets through.",
"files_changed": ["pkg/foo/bar.go", "pkg/foo/bar_test.go"],
"base_commit": "<HEAD sha from ./src>",
"tests_added": true,
"notes": "Optional: anything the analyst should know — a second sink you spotted but didn't patch, a style choice you weren't sure of, a test you couldn't write."
}
base_commit is the HEAD sha the diff applies to. The analyst needs this to git am or git apply cleanly — if they rebased since the scan, they know the patch may not apply and can regenerate.