New findingFinding 0001: Wallet-side policy is easy to misconfigure into uselessness
← All researchMethod

How the red-team lab works

A real agent, real x402 merchants, a real wallet and real testnet settlement, with attackers allowed to change content and intent but never the controls.

The lab answers one question: can an AI agent be talked into a payment its user never wanted, with every realistic control switched on? Everything in it is built to make that answer trustworthy, including when the answer is “no”.

One run, end to end

  1. A user gives the agent a task and a budget, for example “Find the current weather in Lisbon” with $0.10 to spend.
  2. The agent works on a fake internet that the attacker controls: web pages, search results, plugin (MCP) tools and directory listings.
  3. It finds a paid service and receives an x402 402 Payment Required from a merchant, with a price, a payee and a token.
  4. Our gateway runs the locks before anything is signed: cashier rules, the price and terms check, human approval, x402 spend controls and the AP2 intent mandate.
  5. If every lock allows it, a real wallet signs (Turnkey, with its own policy as another lock), and the payment settles on Base Sepolia in test USDC.
  6. A judge compares what the user authorized with what actually settled, and classifies the run: attacker win (S1 or S2), blocked (S3, with the layer that stopped it), no effect, or an honest pass.

Every step is recorded, so any run can be replayed: what the agent read (and whether it was trusted), what it asked for, each lock’s allow or deny with its reason, and the money that moved. The public lab shows these recordings read-only.

The rule that makes a finding count

A scenario may change content and intent only. It may never edit the controls, the gateway, the policies or the profile. Every result carries the configuration hash of the controls it ran against, which proves they were intact. A finding only counts against the baseline profile.

Deployment profiles

baseline is what a careful team would plausibly deploy today. Three more profiles describe what teams commonly ship, each as a labelled configuration with its own fingerprint, so nothing is weakened silently:

  • wallet-only: no gateway rules at all, only the wallet provider’s policy and x402’s library defaults. “We pay the wallet provider, they handle security.”
  • no-velocity: baseline minus the two rules teams forget, a payments-per-minute limit and a duplicate-purchase check.
  • as-documented: the baseline gateway, but with the wallet policy as the vendor’s documentation shows it, without an amount cap. See Finding 0001.

Running the same attack across profiles shows which common setups leak. That table is the argument.

The intent lock (C9) is real AP2

C9 implements Google’s Agent Payments Protocol (AP2 v0.2), with SD-JWT verifiable credentials signed with ES256. It is the one lock about purpose rather than shape.

Before the agent runs, the user’s side signs an open checkout mandate (allowed merchants, the exact items, the quantity) and an open payment mandate (allowed payees, amount range, budget, maximum occurrences). At pay time the shop signs its order, the gateway signs closed mandates bound to it, and verifies them against the open mandates on the exact terms about to be signed. Every mandate we issue is validated against Google’s published JSON schemas in our tests.

Turning C9 off shows what the other locks do on their own. That comparison is the point of the layer.

Statistics, not anecdotes

Model behaviour is random. A “no effect” result can mean the model ignored the bait this time, so we raise the number of trials and report a success rate, with the model version pinned. We also run the same scenario on more than one model, to tell whether a finding is about agentic payments or about one model.