Problem
loopover_find_opportunities returns attacker-authored GitHub issue titles verbatim into a model that
holds local write tools.
toRankedEntry (src/mcp/find-opportunities.ts:152-175) copies issue.title straight from the GitHub
API into the tool result. toolResult (src/mcp/server.ts:5444-5455) embeds it in both
content[0].text and structuredContent. The only transform on the way out is redactSensitiveForMcp
(:5458-5466), which filters key names against /hotkey|coldkey|wallet|…/ and never touches string
values. There is no instruction-neutralisation, no delimiter fencing, no length truncation.
Contrast getAgentAuditFeed (src/mcp/server.ts:5127), which explicitly runs sanitizePublicComment
over its one free-form field as "defense-in-depth". The same class of field is unscrubbed here.
loopover_check_before_start and loopover_validate_linked_issue return a report built from the same
issue metadata.
Trigger
Anyone opens an issue on a repo in MCP_READ_REPO_ALLOWLIST with a title carrying instructions. A miner's
agent calls loopover_find_opportunities, reads the title as part of its own context, and can then be
steered into any of the local write tools — loopover_open_pr, loopover_file_issue,
loopover_close_pr, loopover_delete_branch.
Those write tools carry no auth gate of their own (this.localWriteSpec(...),
src/mcp/server.ts:2788-2843); they are pure spec builders, so the boundary is entirely the driving
model's judgment.
This is also the indirect reachability path for the baseRef argument-injection tracked privately as
GHSA-v3j4-j27j-fxw6 — that advisory owns the injection sink; this issue owns the source.
Impact
A public issue title is a direct write into the decision loop of an agent operating on the contributor's
own GitHub credentials.
Dedup
Distinct from #9035, which covers the AI-reviewer prompt path (ORB's own model, reviewing a PR). This
is the MCP tool-result path — a different producer and a different consumer.
Requirements
- Run titles (and any other free-form upstream text in MCP results) through
sanitizePublicComment, or a
stricter scrub that strips imperative markup, fences, and HTML comments.
- Truncate to ~120 chars.
- Label the field as untrusted in the tool description so the client model treats it as data rather than
instruction.
- Sweep the other MCP tools returning upstream free-form text (
check_before_start,
validate_linked_issue) for the same gap, and add a shared helper so the next tool cannot omit it.
Test Coverage Requirements
99%+ patch coverage, branch-counted; a title containing injection markup asserted neutralised in both
content and structuredContent.
Links & Resources
maintainer-only — MCP trust boundary.
Problem
loopover_find_opportunitiesreturns attacker-authored GitHub issue titles verbatim into a model thatholds local write tools.
toRankedEntry(src/mcp/find-opportunities.ts:152-175) copiesissue.titlestraight from the GitHubAPI into the tool result.
toolResult(src/mcp/server.ts:5444-5455) embeds it in bothcontent[0].textandstructuredContent. The only transform on the way out isredactSensitiveForMcp(
:5458-5466), which filters key names against/hotkey|coldkey|wallet|…/and never touches stringvalues. There is no instruction-neutralisation, no delimiter fencing, no length truncation.
Contrast
getAgentAuditFeed(src/mcp/server.ts:5127), which explicitly runssanitizePublicCommentover its one free-form field as "defense-in-depth". The same class of field is unscrubbed here.
loopover_check_before_startandloopover_validate_linked_issuereturn areportbuilt from the sameissue metadata.
Trigger
Anyone opens an issue on a repo in
MCP_READ_REPO_ALLOWLISTwith a title carrying instructions. A miner'sagent calls
loopover_find_opportunities, reads the title as part of its own context, and can then besteered into any of the local write tools —
loopover_open_pr,loopover_file_issue,loopover_close_pr,loopover_delete_branch.Those write tools carry no auth gate of their own (
this.localWriteSpec(...),src/mcp/server.ts:2788-2843); they are pure spec builders, so the boundary is entirely the drivingmodel's judgment.
This is also the indirect reachability path for the
baseRefargument-injection tracked privately asGHSA-v3j4-j27j-fxw6 — that advisory owns the injection sink; this issue owns the source.
Impact
A public issue title is a direct write into the decision loop of an agent operating on the contributor's
own GitHub credentials.
Dedup
Distinct from #9035, which covers the AI-reviewer prompt path (ORB's own model, reviewing a PR). This
is the MCP tool-result path — a different producer and a different consumer.
Requirements
sanitizePublicComment, or astricter scrub that strips imperative markup, fences, and HTML comments.
instruction.
check_before_start,validate_linked_issue) for the same gap, and add a shared helper so the next tool cannot omit it.Test Coverage Requirements
99%+ patch coverage, branch-counted; a title containing injection markup asserted neutralised in both
contentandstructuredContent.Links & Resources
src/mcp/find-opportunities.ts~152-175;src/mcp/server.ts~2788-2843, ~3908-3918, ~5127, ~5444-5466maintainer-only — MCP trust boundary.