#11740: fix(gateway): remove IP-based canvas auth fallback
gateway
stale
Cluster:
Security Enhancements and Fixes
## Fix Summary
Remove `hasAuthorizedWsClientForIp()` which allowed cross-user canvas authorization bypass in shared-IP environments (NAT, corporate proxy, shared hosting). When one user had an authenticated WebSocket connection from an IP, any other user on the same IP could access canvas endpoints without credentials.
Canvas requests now require either:
- Direct local access (loopback), or
- Explicit Bearer token authentication
**Changes:**
- Remove `hasAuthorizedWsClientForIp` function from `server-http.ts`
- Remove IP-based fallback in `authorizeCanvasRequest` — return `false` after bearer token check
- Remove unused imports (`getHeader`, `resolveGatewayClientIp`)
- Update e2e tests to expect 401 for IP-only auth attempts
## Issue Linkage
Fixes #11738
## Security Snapshot
- CVSS v3.1: 8.9 (High)
- CVSS v4.0: 9.5 (Critical)
## Implementation Details
### Files Changed
- `src/gateway/server-http.ts` (+3/-22)
- `src/gateway/server.canvas-auth.e2e.test.ts` (+26/-21)
### Technical Analysis
Remove `hasAuthorizedWsClientForIp()` which allowed cross-user canvas authorization bypass in shared-IP environments (NAT, corporate proxy, shared hosting). When one user had an authenticated WebSocket connection from an IP, any other user on the same IP could access canvas endpoints without credentials.
## Validation Evidence
- Command: `server.canvas-auth.e2e.test.ts`
- Status: passed
## Risk and Compatibility
non-breaking; compatibility impact was not explicitly documented in the original PR body.
## AI-Assisted Disclosure
- AI-assisted: yes
- Model: Claude Code
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR removes the IP-based fallback (`hasAuthorizedWsClientForIp`) for authorizing canvas HTTP/WS requests, tightening access control so canvas endpoints require either a loopback/direct-local request or a valid Bearer token. It also updates the canvas auth e2e test to expect 401s for IP-only attempts and adds a positive assertion that Bearer token auth still succeeds.
Overall, the gateway’s `authorizeCanvasRequest` path in `src/gateway/server-http.ts` is simplified to a clear allowlist (local direct or bearer auth) which aligns with the stated security goal of preventing cross-user authorization in shared-IP environments.
<h3>Confidence Score: 4/5</h3>
- This PR is close to safe to merge; main remaining concern is the WS e2e regression test can pass without asserting the intended 401 behavior.
- The core security change (removing shared-IP fallback authorization) is straightforward and consistently applied to both HTTP and WS authorization paths. However, the updated e2e helper currently treats any WebSocket error as a successful rejection, which can mask failures and reduce confidence that the WS auth regression is actually being exercised in CI.
- src/gateway/server.canvas-auth.e2e.test.ts
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23355: Gateway: fail closed on untrusted proxy headers
by bmendonca3 · 2026-02-22
78.9%
#19937: fix(gateway): validate token/password auth modes and isolate gatewa...
by NewdlDewdl · 2026-02-18
77.7%
#19885: test(gateway,browser): isolate tests from ambient OPENCLAW_GATEWAY_...
by NewdlDewdl · 2026-02-18
77.1%
#23361: Gateway: reject scope assertions without identity binding
by bmendonca3 · 2026-02-22
76.7%
#17705: fix(gateway): allow trusted-proxy auth to bypass device-pairing gates
by dashed · 2026-02-16
76.7%
#15603: fix(gateway): correct malformed HTTP 429 response on WebSocket upgrade
by AI-Reviewer-QS · 2026-02-13
76.4%
#21772: [Bug]: Allow ws:// to Tailscale CGNAT addresses
by AIflow-Labs · 2026-02-20
76.2%
#12802: fix(gateway): default unscoped operator connections to read-only
by yubrew · 2026-02-09
76.1%
#22381: Security/Gateway: block cross-origin silent auto-pairing in auth mo...
by bmendonca3 · 2026-02-21
75.9%
#8513: Gateway: require auth for plugin HTTP
by coygeek · 2026-02-04
75.8%