High

MCP Tool Returns Untrusted External Content Carrying Hidden Agent Instructions Without Spotlighting

An AI agent that reviews pull requests can be tricked by text hidden inside the PR description itself. Because one tool in the Microsoft Azure DevOps MCP server returns that text raw and unfiltered, an attacker can bury commands in an invisible HTML comment that the agent reads and obeys, using the reviewer's own access.

How the attack works

An attacker writes a pull request description containing an HTML comment or CSS-hidden text block that a human never sees in the web UI. The MCP tool repo_get_pull_request_by_id fetches this description and passes it straight to the AI agent as raw JSON, unlike other tools in the same server that wrap external content in a safety marker first. The agent reads the hidden comment as an instruction, not as data, and carries it out with the reviewer's own credentials — for example, fetching content from a project the attacker has no access to. The agent can then be directed to paste the stolen output back into a PR comment where the attacker can read it.

Netzilo detection

Why it matters

An organization loses control over what its review agent does and what it can reach: an outside contributor can make the agent act as their proxy inside private projects, exfiltrating data the attacker was never authorized to see.

What you can do

  • Check whether MCP tools that return third-party text (PR descriptions, comments, wiki pages, commit messages, build logs) wrap that content in a clear delimiter before it reaches the model, and flag any tool that does not.
  • Have the agent's reviewer treat all fetched external content as untrusted data, not instructions, and refuse to act on directives embedded in it.
  • Scan incoming PR descriptions, comments and commit messages for HTML comments or CSS-hidden text (display:none, font-size:0, white-on-white) before they reach an AI reviewer.
  • Restrict what a review agent can do with the credentials of the human reviewer — for example, don't let it access other projects or post comments without a human confirming the action.

Known benign look-alikes

  • Documentation, changelogs and README content returned by a docs/wiki MCP tool that legitimately contains HTML comments used for authoring directives (for example a comment reading 'do not edit, generated file') — these carry no second-person imperative aimed at an agent.
  • Static-site and Markdown tooling output containing template comments such as <!-- prettier-ignore -->, <!-- markdownlint-disable -->, <!-- more --> or Jekyll/Hugo front-matter comments.
  • Security research write-ups, disclosure posts and conference material about this very technique returned by a web-fetch or wiki MCP tool: prose describing that an attacker hides instructions in a PR description is third-person reportage, not a live payload. Layer 5 is bounded to imperative authoring verbs to keep this class out, but a quoted PoC snippet inside a research article WILL fire Layers 1-4 by design, because a quoted payload re-entering agent context is itself the risk.
  • Content that IS correctly spotlighted but still carries an embedded imperative: Layers 1-3 fire on the carrier regardless of the wrapper. This is intentional (spotlighting mitigates but does not neutralise an injection attempt) but means those layers do not, on their own, prove that spotlighting was missing. Only Layer 4 asserts the raw-serialization path.
  • Templated PR/issue bodies generated by bots (dependabot, release-please) that embed HTML comments holding machine-readable metadata; these lack agent-directed imperative verbs.

References

Related threats