e06f9d5670e091cf875803a793695d2af0973fb0
GET /api/keys returned each key's AssignedCount as a raw CountDocuments over every non-revoked assignment, with no scope filter — a tag-restricted token reading the list saw a nonzero count for a key it can see nothing assigned to in its own scope, which is enough to tell it an assignment exists on a host it must not know about. Same class of leak getKey's assignment-list filter closed on the detail route, surviving on the list route through a count instead of a server object. services.ListKeys now takes the caller's tokenScope. An unrestricted caller (empty scope) takes the original unfiltered per-key CountDocuments with no extra work, so the common case is not slower. A restricted caller resolves the visible fleet once via ListServers before the per-key loop, then counts each key's assignments with an added server_id $in filter — one extra query total, not one per key. ListKeys had exactly one caller (listKeys), so the parameter went there rather than adding a second entry point. Recorded GET /api/keys in serverScopedRoutes as true; its path, like GET /api/keys/:id, matches none of serverTouchingRoutes' substrings, so the entry is not boot-enforced. Deliberately did not widen the filter to catch "keys" — that would sweep in create/delete/private-key routes with no server data at all. The real fix for this shape of gap is the declare-by-default inversion already recorded as a follow-up.
Description
No description provided
9.7 MiB
Languages
JavaScript
63.9%
Go
19.3%
TypeScript
16.6%