Skip to content

orb(mcp): find_opportunities returns attacker-authored issue titles verbatim into a model that holds local write tools — no scrub, no fencing, no truncation #9163

Description

@JSONbored

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.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions