Skip to content

MCP tools pass ids straight into inject URLs, so ../ can reach a different route #8716

Closed

Description

@cstns

Most MCP tools build the inject URL by dropping args.* ids into the path, and most of those ids are just z.string(). app.inject resolves dot segments, so an id like ../applications/<id> ends up hitting a completely different route.

Found it while reviewing #8693: platform_delete_instance with { instanceId: "../applications/<id>", instanceType: "remote" } deleted a whole application on a local platform and answered { status: "okay" }. ../teams/<id> would reach the team delete route the same way. Normal permission checks still apply, so it's not a privilege escalation, but it lets a tool act well outside what it says it does, which matters with prompt injected ids.

#8693 fixes it for that one tool by checking the id shape in the handler. Ids declared as a UUID are already safe since the gateway enforces pattern (see #8575). Everything else is open, and there are roughly 100 places across forge/ee/lib/mcp/tools/ that interpolate args into URLs.

Might be worth considering a shared guard, either a hashid schema with a pattern in schemas.js or encoding path segments in one spot, so each tool doesn't have to remember it.

Metadata

Metadata

Assignees

Labels

area:aiAI relatedtype:bugSomething isn't working

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions