← Back to PRs

#22365: fix(gateway): auto-approve loopback scope upgrades

by AIflow-Labs open 2026-02-21 02:36 View on GitHub →
gateway size: S
## What changed - In WebSocket connect pairing logic, treat loopback client pairing upgrades as silent when they come from the gateway listener itself (`remoteAddr` is loopback), allowing role/scope upgrade handshakes to auto-approve without interactive pairing. - Added regression coverage in `src/gateway/server.auth.e2e.test.ts` to verify loopback paired-device scope upgrades can proceed without prompts. - Adjusted the existing scope-upgrade test to assert interactive pairing is still required for non-local host connections. ## Why this fixes it Subagent/session calls in loopback token mode can require higher privileges than a previously paired read-scope session used earlier, causing `pairing required` handshakes during `sessions_spawn`. For loopback connections, the server now treats those upgrades as local auto-approve paths, matching operator expectations for localhost tooling. ## Tests - `pnpm vitest run src/gateway/auth.test.ts` - `pnpm vitest run --config vitest.e2e.config.ts src/gateway/server.auth.e2e.test.ts -t "scope upgrades"` - Note: e2e command is currently blocked in this environment by missing optional module (`@discordjs/voice`), so full e2e pairing assertion could not complete here. ## Edge cases handled - Non-local host connections still require interactive approval for scope upgrades. - Existing not-paired local loopback behavior remains unchanged (and still auto-approves). - Paired metadata is still updated after successful upgrades. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR aims to auto-approve device pairing scope/role upgrades for loopback connections, addressing friction when subagent/session calls in loopback token mode require higher privileges. The approach introduces `isLocalLoopbackClient` (checking only `isLoopbackAddress(remoteAddr)`) alongside the existing `isLocalClient` (`isLocalDirectRequest`) to broaden which connections get silent (auto-approved) pairing. **Key issues found:** - **Proxy-header bypass in `isSilentPairing` logic:** The new `isLocalLoopbackClient` variable does not account for untrusted proxy headers, unlike the existing `isLocalClient` which explicitly guards against this. When the gateway runs behind a local reverse proxy, `remoteAddr` is loopback even for remote clients, meaning scope/role-upgrade pairing would be silently auto-approved. Adding a `!hasUntrustedProxyHeaders` guard to the `isLocalLoopbackClient` branch would preserve the intended behavior while maintaining the existing security invariant. - **Test likely fails with the new logic:** The modified test "requires pairing for scope upgrades from non-local host" connects to `ws://127.0.0.1:${port}` with only a Host header override. Since `remoteAddr` remains loopback, `isLocalLoopbackClient` would be `true`, causing silent auto-approval — contradicting the test's assertion that `res.ok === false`. The PR notes the e2e tests could not be run in the author's environment. <h3>Confidence Score: 2/5</h3> - This PR has a likely proxy-header bypass that could allow silent privilege escalation for remote clients behind a local reverse proxy, and an e2e test that appears to contradict the new logic. - Score of 2 reflects two concrete issues: (1) the `isLocalLoopbackClient` check bypasses the proxy-header safety guard that `isLocalDirectRequest` enforces, creating a potential privilege escalation path for remote clients routed through a local proxy, and (2) the modified scope-upgrade test likely fails because the test connects via loopback (`remoteAddr` = 127.0.0.1) while asserting non-local behavior. The e2e tests were not run by the author. - `src/gateway/server/ws-connection/message-handler.ts` (lines 658-659) — the `isSilentPairing` logic needs a proxy-header guard. `src/gateway/server.auth.e2e.test.ts` (lines 956-967) — test assertion likely conflicts with the new logic. <sub>Last reviewed commit: eae8774</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs