#9146: Fix: Allow null-origin WebSocket connections from loopback
gateway
stale
Fixes #9101
## Problem
The lane-watchdog plugin's auto-resume fails because internal WebSocket connections from loopback (127.0.0.1/localhost) don't include an Origin header, causing the origin check to reject them with 'origin not allowed'.
### Error
```
[lane-watchdog] Connect failed: origin not allowed (open the Control UI from the gateway host or allow it in gateway.controlUi.allowedOrigins)
[lane-watchdog] Auto-resume failed for agent:main:main
```
## Root Cause
In `checkBrowserOrigin()`, when the origin is null/undefined (internal connections don't send Origin headers), the function immediately returns `{ok: false}` even for loopback requests.
## Solution
Allow null-origin requests from loopback hosts by checking if the request is coming from a loopback address (127.0.0.1, localhost, ::1) before rejecting missing origins.
### Implementation
When `parsedOrigin` is null, the code now:
1. Extracts the request hostname
2. Checks if it's a loopback address
3. Returns `{ok: true}` if loopback, otherwise returns `{ok: false}` as before
## Changes
- Modified `checkBrowserOrigin()` in `src/gateway/origin-check.ts` to allow missing/null origins from loopback
- Added 3 test cases:
- ✅ Missing origin from loopback (should accept)
- ✅ Null origin from localhost (should accept)
- ✅ Missing origin from non-loopback (should reject)
## Testing
All tests pass (7/7):
```
✓ src/gateway/origin-check.test.ts (7 tests)
Tests 7 passed (7)
```
## Security Considerations
This change only affects requests from loopback addresses (127.0.0.1, localhost, ::1), which are already considered trusted. Remote requests with missing origins are still rejected as before.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR updates the gateway WebSocket/browser origin check to allow connections that omit the `Origin` header when (and only when) the request is coming from a loopback host (e.g. `127.0.0.1`, `localhost`, `::1`). This addresses internal/loopback clients (like lane-watchdog auto-resume) that legitimately send a null/missing Origin.
The change is localized to `src/gateway/origin-check.ts` by adding a loopback hostname check in the `parsedOrigin == null` path, and is covered by new unit tests in `src/gateway/origin-check.test.ts` for both allowed (loopback) and rejected (non-loopback) missing-origin cases.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is narrowly scoped: it only alters behavior when `Origin` is missing/invalid and then only allows the request if the request host resolves to a loopback address. Existing allowlist and same-origin checks remain unchanged, and the new behavior is covered by targeted unit tests.
- No files require special attention
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#10930: fix: validate WebSocket Origin for all client types, not just brows...
by OneZeroEight-ai · 2026-02-07
83.3%
#23352: Gateway: enforce origin checks for browser-context WS clients
by bmendonca3 · 2026-02-22
78.4%
#22381: Security/Gateway: block cross-origin silent auto-pairing in auth mo...
by bmendonca3 · 2026-02-21
76.9%
#22056: fix(gateway): use loopback for self-connections regardless of bind ...
by usedhonda · 2026-02-20
75.4%
#22110: fix(tools): prefer loopback for internal tool-to-gateway RPC calls
by pierreeurope · 2026-02-20
75.2%
#22365: fix(gateway): auto-approve loopback scope upgrades
by AIflow-Labs · 2026-02-21
73.2%
#23690: fix(gateway): subagent sessions fail with pairing required on loopb...
by yinghaosang · 2026-02-22
72.4%
#17760: fix(browser): rewrite 0.0.0.0 and [::] wildcard addresses in CDP We...
by joeharouni · 2026-02-16
72.3%
#19026: fix(gateway): use loopback for local CLI-to-gateway connections
by Phineas1500 · 2026-02-17
71.7%
#21186: fix(gateway): strict loopback guard for Control UI (v2)
by dinakars777 · 2026-02-19
71.2%