GET /v1/repos/:owner/:repo/pulls/:number/maintainer-packet (src/api/routes.ts ~3432-3433) gates only with requireStaticProtectedApiToken, which admits any static identity including mcp (~6447-6453). It never checks isMcpReadRepoAllowed.
All three sibling routes using the same gate DO check it:
| Route |
allowlist check |
/pulls/:number/maintainer-packet (~3432) |
missing |
/pulls/:number/reviewability (~3456) |
present (~3460) |
/gate-config/effective (~3526) |
present |
/live-gate-thresholds (~3554) |
present |
The MCP tool this route explicitly mirrors is scoped — src/mcp/server.ts ~3724-3734 calls canAccessRepo → isMcpReadRepoAllowed (~4106-4116). So this is precisely the failure the codebase names elsewhere: the HTTP surface silently grants what the MCP surface denies for the same token (routes.ts ~6462-6464).
Impact if reached: the full maintainer packet — all repo issues, all PRs, per-PR files, reviews, check summaries, recent merged PRs. Amplified because MCP_READ_REPO_ALLOWLIST is fail-closed by default (unset ⇒ deny all, src/auth/security.ts ~224-229), so the intended posture is "deny everything" while this route allows everything. The shipped CLI proxies this exact route (packages/loopover-mcp/bin/loopover-mcp.ts ~1764).
Current exposure: LOW — not remotely reachable. Verified: the Cloudflare tunnel exposes only ^/loopover/shot.*$; this path returns 404 from the internet, and 8787 is bound to 127.0.0.1. Exploitation requires the shared MCP token AND network access to the box (tailnet/localhost).
Why fix it anyway: the token is documented as a shared, end-user-obtainable CLI credential, and the planned hosted Orb removes the network isolation that is currently doing all the work. This is a one-line omission that becomes critical the moment the API is exposed.
Fix
Copy the allowlist check from the reviewability route into this handler. Better: fold it into requireStaticProtectedApiToken via an optional repoFullName argument so a future route cannot forget it.
GET /v1/repos/:owner/:repo/pulls/:number/maintainer-packet(src/api/routes.ts~3432-3433) gates only withrequireStaticProtectedApiToken, which admits any static identity includingmcp(~6447-6453). It never checksisMcpReadRepoAllowed.All three sibling routes using the same gate DO check it:
/pulls/:number/maintainer-packet(~3432)/pulls/:number/reviewability(~3456)/gate-config/effective(~3526)/live-gate-thresholds(~3554)The MCP tool this route explicitly mirrors is scoped —
src/mcp/server.ts~3724-3734 callscanAccessRepo→isMcpReadRepoAllowed(~4106-4116). So this is precisely the failure the codebase names elsewhere: the HTTP surface silently grants what the MCP surface denies for the same token (routes.ts~6462-6464).Impact if reached: the full maintainer packet — all repo issues, all PRs, per-PR files, reviews, check summaries, recent merged PRs. Amplified because
MCP_READ_REPO_ALLOWLISTis fail-closed by default (unset ⇒ deny all,src/auth/security.ts~224-229), so the intended posture is "deny everything" while this route allows everything. The shipped CLI proxies this exact route (packages/loopover-mcp/bin/loopover-mcp.ts~1764).Current exposure: LOW — not remotely reachable. Verified: the Cloudflare tunnel exposes only
^/loopover/shot.*$; this path returns 404 from the internet, and 8787 is bound to 127.0.0.1. Exploitation requires the shared MCP token AND network access to the box (tailnet/localhost).Why fix it anyway: the token is documented as a shared, end-user-obtainable CLI credential, and the planned hosted Orb removes the network isolation that is currently doing all the work. This is a one-line omission that becomes critical the moment the API is exposed.
Fix
Copy the allowlist check from the
reviewabilityroute into this handler. Better: fold it intorequireStaticProtectedApiTokenvia an optionalrepoFullNameargument so a future route cannot forget it.