Agent Data Injection: Arbitrary Click Attack against Web Agents (Part 1 of 3)

8 minute read

Published:

This series is based on our paper, Agent Data Injection Attacks are Realistic Threats to AI Agents.

By planting a single fake product review on a shopping page, we made three web agents — Claude for Chrome (Anthropic), Antigravity (Google), and Nanobrowser — click buttons their users never intended. On Claude for Chrome, a request as harmless as “summarize the reviews on this page” ended with a one-click order placed for a product the user never asked for — and it still works on the latest models, Claude Opus 4.8 and Claude Sonnet 5 included. No malware, no phishing, no stolen password: just a product review that hijacked the agent’s next click.

This is essentially an XSS-like attack on web agents. In other words, our attack effectively gives any website that shows user-generated content (reviews, comments, issue threads) an XSS-like vulnerability, including popular, otherwise-secure sites. The attack needs only an ordinary account with no special access. The arbitrary click attack is one instance of a broader class of vulnerability we call Agent Data Injection (ADI), in which attacker-controlled data is misread by the agent as trusted data. To further understand this vulnerability, see our paper.

Arbitrary Click Attack on a Web Agent

You ask a web agent to summarize the customer reviews on a shopping page — a routine, read-only task. First, let’s see how a web agent normally handles a request like this (Figure 1).

The benign web-agent workflow Figure 1 — The benign workflow. The agent reads the page into a summary, the LLM picks an element by its identifier (here [ref_13], “Next Page”), and the agent clicks the matching element.

To carry out your request, the agent first reads the page. In every agent we tested, a page-reading tool (we’ll call it read_page) turns the raw HTML into a summary the LLM can reason about: it strips the HTML tags and gives each remaining element a sequential identifier, like [ref_1] or [ref_2].

A shopping page and the summary read_page produces Figure 2 — A shopping page (left) and the summary read_page produces for the LLM (right). Each element becomes one entry tagged with an identifier — for instance, the “Buy Now” button is [ref_9].

The LLM reads this summary and decides which element to act on, referring to it by identifier. To go to the next page it emits left_click([ref_13]), and the agent clicks the real element that [ref_13] points to.

Now comes the attack (Figure 3). The attacker is an ordinary user who can post a review. In their review they inject text that imitates the summary format: an escaped quote (\") to close their own review entry, a newline, and a fake button "Read More" [ref_9] that reuses the identifier of the real “Buy Now” button.

The attack web-agent workflow Figure 3 — The attack workflow. The injected review conjures a fake button "Read More" [ref_9] that reuses the “Buy Now” identifier, so the LLM’s click — meant only to expand the review — lands on “Buy Now” instead.

Why does the agent fall for it? The \" closes the attacker’s own quoted review text and the newline starts a fake new entry, so the LLM reads the single review as three separate ones — conjuring a button that was never on the page (Figure 4). We call this trick probabilistic delimiter injection.

The injected review, and how the LLM misinterprets it Figure 4 — Left: the summary as read_page actually structures it — the injected text is all part of the attacker’s single review entry ([ref_12]). Right: how the LLM interprets it — the review splits into three entries, conjuring a fake button "Read More" [ref_9] that was never on the page.

Because IDs are assigned in DOM order, the attacker can predict that [ref_9] is the real “Buy Now” button and reuse it. So when the LLM — still dutifully summarizing reviews — clicks the fake “Read More” to expand the review, it emits left_click([ref_9]), and the agent clicks the real “Buy Now” button. An unintended purchase goes through.

Throughout, the agent is doing exactly what you asked: summarizing reviews. The attack corrupted just one piece of data it trusted — an element identifier — and that was enough to redirect a click. And it’s not limited to purchases: wherever element IDs map to sensitive actions, the same redirection applies.

Affected web agents

We reproduced this attack on three real-world web agents: Claude for Chrome (Anthropic), Antigravity (Google), and Nanobrowser. (Antigravity is primarily a coding agent, but it ships a web-browsing feature, which is what we tested.) All of them assign sequential, predictable element identifiers — which is exactly what makes the target ID guessable.

Antigravity and Nanobrowser were tested in January 2026. We re-tested Claude for Chrome in July 2026 on Anthropic’s latest models — Claude Sonnet 5 and Claude Opus 4.8 — and the attack still succeeds. That it lands on Opus 4.8, one of the most capable models available, is the point: a more capable model reads the page structure the same way, so reaching for a stronger model doesn’t close the hole.

Our attack failed against ChatGPT Atlas. It uses randomized nonce identifiers (e.g. ref_4af2b1c9) instead of sequential ones, so the attacker cannot predict the target ID. We discuss this defense in our paper.

Proof of concept: Claude for Chrome

Here is the full Claude for Chrome run, end to end. The video shows the whole attack; the screenshots below break it down step by step.

Video — The full attack against Claude for Chrome (running Claude Opus 4.8). The agent is asked only to summarize the page’s reviews, yet the injected review redirects its very next click onto the 1-Click “Buy Now” button, and an order the user never requested goes through.

The attacker’s payload is nothing but an ordinary-looking product review.

The attacker's review on the product page Figure 5 — The attacker’s review on the test e-commerce page. To a person it reads like a truncated review; its body carries the injected fake-button entry.

You might hope a human-in-the-loop check would catch this, but it doesn’t. Claude for Chrome doesn’t ask for approval at click time; it shows a plan up front, and once you approve it, the agent acts on its own. That plan is built before the LLM reads the page, so it lists only the benign task you asked for — reading and summarizing the reviews — and never mentions a purchase.

Claude for Chrome's pre-task plan Figure 6 — The plan Claude for Chrome asks you to approve before it acts. It lists only benign steps — locate the reviews, extract each one, summarize — with no hint of the “Buy Now” click that will actually happen.

The agent then reads the page, sees the (fake) “Read More,” and clicks it to expand the review. The click lands on the real 1-Click “Buy Now” button, and an “Order Placed” dialog appears.

Strikingly, the model does realize it was tricked — but only after the fact. Having already placed the order, it recognizes the disguised purchase button and the injected review, and correctly flags a prompt-injection-style attack. It even refuses to click “OK” — but “OK” only dismisses the receipt; the one-click order went through the moment it clicked “Buy Now.” Its after-the-fact caution can’t un-click the purchase.

The agent's run: plan, click, and the resulting purchase Figure 7 — The agent (Opus 4.8) clicks the fake “Read More” and triggers the “Order Placed” dialog (right). Only afterward does it recognize the disguised purchase button and the injected review — too late to undo the click.

Responsible disclosure

We reported all findings to the vendors in January 2026. Among the affected web-agent vendors, Anthropic and Google acknowledged the attack as valid; Nanobrowser has not responded.

Conclusion

A single product review was enough to redirect an agent’s click into an unintended purchase — and the same trick works wherever a web agent maps predictable element IDs to sensitive actions. This is one instance of Agent Data Injection: the agent faithfully does your task, but on data an attacker forged. Existing prompt-injection defenses, built to stop malicious instructions, don’t catch it.

In the next post, we forge a different trust anchor — the author identity a coding agent uses to decide whose instructions to trust — and turn it into remote code execution on a developer’s machine.

Next: Part 2 — Forging the Trust Anchor: Origin Injection in Coding Agents (To Be Continued).