Summary
The cost-confirmation safeguard for creating paid resources doesn't provide the guarantee its tool schemas state. confirm_cost performs no human interaction, and the confirm_cost_id it returns is a deterministic hash of a public, low-entropy cost object — so an autonomous or prompt-injected agent can satisfy the gate (or precompute the token and skip confirm_cost entirely) and create billable resources with no human ever confirming costs.
Responsible-disclosure note: I've omitted the literal token values below. They're trivially derivable from the open-source code — I'm just not pasting a ready-to-use bypass. Happy to share full details privately if you prefer.
Stated guarantee
create_project / create_branch input schema: "User must confirm understanding of costs before creating a project/branch."
confirm_cost description: "Ask the user to confirm their understanding of the cost … Returns a unique ID … passed to create_project/create_branch."
What the code does
confirm_cost is execute: async (cost) => ({ confirmation_id: await hashObject(cost) }) (account-tools.ts) — no prompt, no elicitation, no human.
create_branch / create_project accept the call iff hashObject(cost) === confirm_cost_id (branching-tools.ts, account-tools.ts).
getBranchCost() is a constant and project cost is only 0 or 10 (pricing.ts), so the token is a fixed, publicly-derivable value.
So the confirmation is (a) never human-gated and (b) precomputable — an agent can pass a correct confirm_cost_id without ever calling confirm_cost or get_cost.
Impact
An agent holding a valid access token can create billable resources (project $10/mo, branch $0.01344/hr) while bypassing the safeguard meant to ensure a human understood the cost. Given the known prompt-injection surface of MCP tools, injected data could drive unbudgeted resource creation with no cost prompt. This is a safety-guardrail weakness (it requires a valid token), not an auth bypass or data exposure.
Reproduction
A ~15-line script re-implementing hashObject + the pricing.ts constants reproduces the confirmation token with no account and no billed calls (the create-time check is a pure string comparison against that value). I did not run the paid create call end-to-end.
Suggested fix
Make confirm_cost a real client-side human checkpoint (MCP elicitation), and/or issue an unforgeable, single-use confirmation nonce (bound to org + timestamp, stored and checked once) instead of a deterministic hash of public data.
Found with llmcontract, a session-type runtime monitor for agent/tool protocols. Write-up (redacted PoC): https://github.com/chrisbartoloburlo/llmcontract-mcp-audit/tree/main/servers/supabase
Summary
The cost-confirmation safeguard for creating paid resources doesn't provide the guarantee its tool schemas state.
confirm_costperforms no human interaction, and theconfirm_cost_idit returns is a deterministic hash of a public, low-entropy cost object — so an autonomous or prompt-injected agent can satisfy the gate (or precompute the token and skipconfirm_costentirely) and create billable resources with no human ever confirming costs.Responsible-disclosure note: I've omitted the literal token values below. They're trivially derivable from the open-source code — I'm just not pasting a ready-to-use bypass. Happy to share full details privately if you prefer.
Stated guarantee
create_project/create_branchinput schema: "User must confirm understanding of costs before creating a project/branch."confirm_costdescription: "Ask the user to confirm their understanding of the cost … Returns a unique ID … passed tocreate_project/create_branch."What the code does
confirm_costisexecute: async (cost) => ({ confirmation_id: await hashObject(cost) })(account-tools.ts) — no prompt, no elicitation, no human.create_branch/create_projectaccept the call iffhashObject(cost) === confirm_cost_id(branching-tools.ts,account-tools.ts).getBranchCost()is a constant and project cost is only0or10(pricing.ts), so the token is a fixed, publicly-derivable value.So the confirmation is (a) never human-gated and (b) precomputable — an agent can pass a correct
confirm_cost_idwithout ever callingconfirm_costorget_cost.Impact
An agent holding a valid access token can create billable resources (project $10/mo, branch $0.01344/hr) while bypassing the safeguard meant to ensure a human understood the cost. Given the known prompt-injection surface of MCP tools, injected data could drive unbudgeted resource creation with no cost prompt. This is a safety-guardrail weakness (it requires a valid token), not an auth bypass or data exposure.
Reproduction
A ~15-line script re-implementing
hashObject+ thepricing.tsconstants reproduces the confirmation token with no account and no billed calls (the create-time check is a pure string comparison against that value). I did not run the paid create call end-to-end.Suggested fix
Make
confirm_costa real client-side human checkpoint (MCP elicitation), and/or issue an unforgeable, single-use confirmation nonce (bound to org + timestamp, stored and checked once) instead of a deterministic hash of public data.Found with llmcontract, a session-type runtime monitor for agent/tool protocols. Write-up (redacted PoC): https://github.com/chrisbartoloburlo/llmcontract-mcp-audit/tree/main/servers/supabase