Why
The target_issue value from an LLM dedup action is forwarded directly to comment_on_issue without any type or bounds check, so a malformed or non-integer value from model output reaches the gh CLI as an argument — causing an unpredictable failure or, in a more adversarial scenario, argument injection.
Current state
loops/scan.py line 84 extracts the value directly:
target = entry["target_issue"]
Line 90 forwards it immediately to comment_on_issue:
comment_on_issue(target, entry["comment_body"])
No isinstance(target, int) check, range guard, or parsing step exists between extraction and use. The value comes from LLM-generated JSON and can be any type the model chooses to emit.
Ideal state
target_issue is validated as a positive integer (e.g. 1 ≤ value ≤ 999999) before being passed to comment_on_issue.
- A non-integer, negative, or missing value raises a clear error that is logged and stops the dedup action without calling
gh.
- The validation happens at the point of extraction from
entry, not inside comment_on_issue.
Starting points
loops/scan.py lines 84 and 90 — where target_issue is extracted and forwarded
loops/common/github.py comment_on_issue — to confirm there is no validation inside the function either
QA plan
- Construct a synthetic dedup action entry with
target_issue set to "not-a-number" and run it through the dedup path — expect a clear validation error before any gh subprocess is started.
- Set
target_issue to -1 — expect the same validation error.
- Set
target_issue to a valid issue number (e.g. 42) — expect normal behaviour with gh issue comment 42 called correctly.
Done when
scan.py raises a clear validation error for any target_issue value that is not a positive integer, before any gh subprocess is started.
Why
The
target_issuevalue from an LLM dedup action is forwarded directly tocomment_on_issuewithout any type or bounds check, so a malformed or non-integer value from model output reaches theghCLI as an argument — causing an unpredictable failure or, in a more adversarial scenario, argument injection.Current state
loops/scan.pyline 84 extracts the value directly:Line 90 forwards it immediately to
comment_on_issue:No
isinstance(target, int)check, range guard, or parsing step exists between extraction and use. The value comes from LLM-generated JSON and can be any type the model chooses to emit.Ideal state
target_issueis validated as a positive integer (e.g.1 ≤ value ≤ 999999) before being passed tocomment_on_issue.gh.entry, not insidecomment_on_issue.Starting points
loops/scan.pylines 84 and 90 — wheretarget_issueis extracted and forwardedloops/common/github.pycomment_on_issue— to confirm there is no validation inside the function eitherQA plan
target_issueset to"not-a-number"and run it through the dedup path — expect a clear validation error before anyghsubprocess is started.target_issueto-1— expect the same validation error.target_issueto a valid issue number (e.g.42) — expect normal behaviour withgh issue comment 42called correctly.Done when
scan.pyraises a clear validation error for anytarget_issuevalue that is not a positive integer, before anyghsubprocess is started.