fix(security): classify new board-key routes

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-09-02 15:22:03 +00:00 committed by cryppadotta
parent bbb25f3b58
commit 64039bb3f5
3 changed files with 10 additions and 2 deletions

View File

@ -207,11 +207,11 @@ export const BOARD_KEY_ROUTE_INVENTORY: readonly string[] = [
"board_key_denied | deny | /api/cases/{*path}",
"board_key_denied | deny | /api/cli-auth/{*path}",
"board_key_denied | deny | /api/cloud/{*path}",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/auth-signal",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/detect-model",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/login-sessions",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/login-sessions/{id}",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/login-sessions/{id}/cancel",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/model-profiles",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/models",
"board_key_denied | deny | /api/companies/{id}/adapters/{id}/test-environment",
"board_key_denied | deny | /api/companies/{id}/agent-configurations",
@ -246,6 +246,7 @@ export const BOARD_KEY_ROUTE_INVENTORY: readonly string[] = [
"board_key_denied | deny | /api/companies/{id}/users/me/inbox-agent-policy",
"board_key_denied | deny | /api/companies/{id}/users/{id}/inbox-agent-policy",
"board_key_denied | deny | /api/companies/{id}/users/{id}/profile",
"board_key_denied | deny | /api/connection-intents/{*path}",
"board_key_denied | deny | /api/feedback-traces/{*path}",
"board_key_denied | deny | /api/health/{*path}",
"board_key_denied | deny | /api/invites/{*path}",
@ -260,6 +261,7 @@ export const BOARD_KEY_ROUTE_INVENTORY: readonly string[] = [
"board_key_denied | deny | /api/tools/oauth/{*path}",
"board_key_denied | deny | /llms/{*path}",
"board_key_denied | deny | /mcp/{*path}",
"board_key_denied | deny | /runtime-tools/{*path}",
"company | activity:read | /api/companies/{id}/{*path}",
"company | agents:operate | /api/agents/{id}/{*path}",
"company | agents:read | /api/agents/{id}/{*path}",
@ -345,6 +347,7 @@ export const BOARD_KEY_ROUTE_INVENTORY: readonly string[] = [
"company | tools:read | /api/tool-connections/{id}/{*path}",
"company | tools:read | /api/tool-gateway/{*path}",
"company | tools:read | /api/tool-profiles/{id}/{*path}",
"company | tools:read | /api/tools/{*path}",
"company | workspaces:manage | /api/execution-workspaces/{id}/{*path}",
"company | workspaces:read | /api/companies/{id}/{*path}",
"company | workspaces:read | /api/execution-workspaces/{id}/{*path}",

View File

@ -56,6 +56,8 @@ describe("board-key route registry", () => {
["DELETE", "/api/board-api-keys/11111111-1111-4111-8111-111111111111", "board_api_keys:revoke_self", "key_self"],
["POST", "/api/board-api-keys", "deny", "board_key_denied"],
["GET", "/api/cli-auth/me", "deny", "board_key_denied"],
["POST", "/runtime-tools/connections/request", "deny", "board_key_denied"],
["POST", "/api/connection-intents/11111111-1111-4111-8111-111111111111/complete", "deny", "board_key_denied"],
["GET", "/api/not-yet-registered", "deny", "undeclared"],
] as const)("classifies %s %s", (method, routePath, action, classification) => {
expect(lookupBoardKeyRoute(method, routePath)).toMatchObject({ action, classification });
@ -70,7 +72,8 @@ describe("board-key route registry", () => {
expect(paths).toContain("/api/companies/:companyId/issues");
expect(paths.some((path) => path.startsWith("/api/companies/:companyId/skills/:skillId/"))).toBe(true);
expect(paths.every((path) => path.startsWith("/api/") || path.startsWith("/_plugins/")
|| path.startsWith("/llms/") || path.startsWith("/mcp/"))).toBe(true);
|| path.startsWith("/llms/") || path.startsWith("/mcp/")
|| path.startsWith("/runtime-tools/"))).toBe(true);
});
it("declares an explicit policy for every reachable route", () => {

View File

@ -305,6 +305,7 @@ export function lookupBoardKeyRoute(methodInput: string, rawPath: string): Board
// non-/api path stays undeclared and fails closed.
if (segments[0] === "_plugins") return denied(method, "/_plugins/{*path}");
if (segments[0] === "llms") return denied(method, "/llms/{*path}");
if (segments[0] === "runtime-tools") return denied(method, "/runtime-tools/{*path}");
if (segments[0] !== "api") {
return declared(method, path, "deny", "undeclared", "none", { concealment: "forbidden" });
}
@ -339,6 +340,7 @@ export function lookupBoardKeyRoute(methodInput: string, rawPath: string): Board
"smoke-lab",
"cases",
"pipelines",
"connection-intents",
"_plugins",
].includes(top)) return denied(method, `/api/${top}/{*path}`);