server/src/computer/routes.ts guards every path under /:botId/* with canUseBot, which is what keeps one person from driving somebody else's Bot. It steps aside for policy and fleet, because those are the router's own paths and Hono matches /* against zero segments, so /policy arrives as a Bot id and would otherwise 404 for everybody including an administrator.
The skip is by name alone, so it covers the whole subtree. /policy/status, /policy/read, /fleet/screenshot and the rest are /:botId/... with a Bot called policy or fleet, and for those the guard is not bypassed so much as never consulted: canUseBot is not called at all.
That matters because a Bot really can have one of those ids. Everything created through the API is agent_<uuid>, but tenant package ids are author-chosen strings (examples/fintech/agents.yaml picks general-assistant, knowledge, risk-analyst), and nothing in tenant-package.ts reserves anything. A package naming a Bot policy hands that Bot's computer surface, its screenshots, its pages, its files, to anybody who can sign in.
Reproducing it needs no database. Build the router with a canUseBot that denies everything, then ask for a Bot called policy:
GET /api/computers/agent_1234/status -> 404, canUseBot asked, gateway not reached
GET /api/computers/policy/status -> 200, canUseBot never asked, gateway reached with "policy"
GET /api/computers/fleet/status -> 200, canUseBot never asked, gateway reached with "fleet"
Two things worth separating. The subtree skip is the defect, and it holds whether or not such a Bot exists today. The package being free to name one is what makes it reachable, and correcting the YAML afterwards does not remove a Bot that already has the name, since nothing deletes a canonical agent when a package stops declaring it.
A fix is on the way in a PR.
server/src/computer/routes.tsguards every path under/:botId/*withcanUseBot, which is what keeps one person from driving somebody else's Bot. It steps aside forpolicyandfleet, because those are the router's own paths and Hono matches/*against zero segments, so/policyarrives as a Bot id and would otherwise 404 for everybody including an administrator.The skip is by name alone, so it covers the whole subtree.
/policy/status,/policy/read,/fleet/screenshotand the rest are/:botId/...with a Bot calledpolicyorfleet, and for those the guard is not bypassed so much as never consulted:canUseBotis not called at all.That matters because a Bot really can have one of those ids. Everything created through the API is
agent_<uuid>, but tenant package ids are author-chosen strings (examples/fintech/agents.yamlpicksgeneral-assistant,knowledge,risk-analyst), and nothing intenant-package.tsreserves anything. A package naming a Botpolicyhands that Bot's computer surface, its screenshots, its pages, its files, to anybody who can sign in.Reproducing it needs no database. Build the router with a
canUseBotthat denies everything, then ask for a Bot calledpolicy:Two things worth separating. The subtree skip is the defect, and it holds whether or not such a Bot exists today. The package being free to name one is what makes it reachable, and correcting the YAML afterwards does not remove a Bot that already has the name, since nothing deletes a canonical agent when a package stops declaring it.
A fix is on the way in a PR.