Skip to content

Add a route-table guard test so new /api/v1 GET routes cannot ship unscoped #1178

Closed

Description

@Dumbris

Summary

Closing #1166 took three passes over the route table, and each pass found routes the previous one missed:

  1. The issue named /api/v1/servers and /api/v1/config.
  2. An adversarial review found /api/v1/tools, /api/v1/index/search, /api/v1/profiles, /api/v1/annotations/coverage, /api/v1/security/scans and /api/v1/status.
  3. A cross-model review then found the entire /api/v1/servers/{id}/* read subtree (tools, logs, tool-calls, scan/*, integrity, tools/export), the whole /api/v1/activity and /api/v1/tool-calls family, /api/v1/sessions, and /api/v1/stats/tokens.

Hand-enumerating a route table does not converge. The fix that ships gates the subtree with a chi middleware so new sub-resources inherit it, but nothing stops a new top-level route being added unscoped.

Proposal

A test that walks the production router (chi.Walk over s.router), collects every GET under /api/v1, and asserts each is either:

  • demonstrably scope-filtered (exercised by a scoped-caller test), or
  • on an explicit allowlist with a written reason (e.g. "admin-only, denied by requireAdminRead", "carries no server identity").

A new route then fails the build until someone classifies it, and the allowlist becomes the reviewable record of what is deliberately unscoped.

Guard against vacuity: the test must assert it found a non-trivial number of routes, or a chi.Walk that silently matches nothing passes forever.

Why it is worth the effort

This is the durable fix for the enumerate-and-patch problem. Two of the routes in pass 3 (/servers/{id}/logs, /activity) leaked strictly more than the enumeration the original issue was about — server stdout/stderr, and tool-call arguments and responses.

Activity

  1. added
    kind/testAdding or improving tests
    area/proxyCore MCP proxy engine and tool routing
    priority/mediumImportant but not blocking a release
    on Sep 2, 2026
  2. Sriram-PR commented on Sep 22, 2026

    @Sriram-PR
    Contributor

    i can take this one. went through how the /api/v1 routes get scoped for agent tokens, the deny cases go through requireAdminRead, and the rest get filtered by visibleServers / the scopedServerSubtree middleware, so the chi.Walk idea fits well here.

    plan: walk s.router, collect every GET under /api/v1, and assert each is either driven by a scoped-token test that proves it's filtered/denied, or on an explicit allowlist with a written reason next to it. a new route that's neither fails until someone classifies it. plus the min-count check so an empty walk can't pass silently.

    one thing worth flagging: there's already a chi.Walk guard for the server edition (tenant sessions) in tenant_allowlist_walk_test.go, but nothing equivalent for the personal-edition agent-token scope, which is where #1166 actually bit. so this fills that gap rather than duplicating it.

  3. Dumbris commented on Oct 6, 2026

    @Dumbris
    MemberAuthor

    Triage on 2026-10-05 against main 8442428: Implemented by merged PR #1351 as internal/httpapi/scope_route_table_guard_test.go: a chi.Walk route table with a minGetRoutes=40 vacuity floor and an allowlist with reasons. The issue stayed open only because the PR had no closing keyword.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/proxyCore MCP proxy engine and tool routingkind/securitySecurity fix or hardeningkind/testAdding or improving testspriority/mediumImportant but not blocking a release

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions