#22365: fix(gateway): auto-approve loopback scope upgrades
gateway
size: S
Cluster:
Device Pairing and Gateway Fixes
## 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
#23708: fix(gateway): auto-approve scope upgrades for loopback clients
by widingmarcus-cyber · 2026-02-22
91.0%
#23690: fix(gateway): subagent sessions fail with pairing required on loopb...
by yinghaosang · 2026-02-22
89.6%
#22838: fix(gateway): auto-approve loopback pairing for scope/role upgrades
by GodsBoy · 2026-02-21
88.2%
#22587: fix(gateway): silently auto-approve local paired-device scope upgrades
by abhishekp76 · 2026-02-21
87.9%
#22280: fix(gateway): silently auto-approve local paired-device scope upgrades
by abhishekp76 · 2026-02-21
87.1%
#17425: fix(gateway): auto-approve scope/role upgrades for already-paired d...
by sauerdaniel · 2026-02-15
86.9%
#22712: fix(gateway): auto-approve all device pairing for localhost connect...
by NewdlDewdl · 2026-02-21
85.9%
#22253: fix: auto-approve local loopback pairing for role/scope upgrades
by cjpraia · 2026-02-20
83.9%
#21697: fix(gateway): unblock local spawn pairing and gated private-LAN ws
by rjuanluis · 2026-02-20
83.4%
#22381: Security/Gateway: block cross-origin silent auto-pairing in auth mo...
by bmendonca3 · 2026-02-21
81.7%