#23361: Gateway: reject scope assertions without identity binding
gateway
size: S
trusted-contributor
## Summary
- reject connect handshakes that request non-empty scopes without a bound device identity
- keep dangerous Control UI bypass behavior unchanged (explicitly opt-in)
- add auth e2e regression coverage for scope-less vs scope-asserting non-device connects
## Why
This prevents client-asserted scope escalation in contexts where the server cannot bind scopes to a device/token identity.
## Tests
- pnpm test src/gateway/origin-check.test.ts src/gateway/net.test.ts
- pnpm test:e2e src/gateway/server.auth.e2e.test.ts
- pnpm tsgo
- pnpm check
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR prevents client-asserted scope escalation by rejecting connect handshakes that request non-empty scopes without a bound device identity. The change converts a silent permission downgrade (where scopes were cleared) into an explicit rejection with error code 1008.
Key changes:
- Gateway websocket handler now rejects connections with `scopes require device identity` instead of silently clearing requested scopes when no device identity is provided
- Control UI bypass behavior (via `dangerouslyDisableDeviceAuth`) remains unchanged but now requires explicit break-glass environment variable `OPENCLAW_UNSAFE_ALLOW_CONTROL_UI_BYPASS=1`
- Added comprehensive e2e test coverage distinguishing scope-less vs scope-asserting non-device connects
- Hardened Control UI path traversal protection with symlink escape detection using `fs.realpathSync` and `isWithinDir` validation
- Enhanced loopback detection across platforms (TypeScript, Kotlin, Swift) to handle IPv4-mapped IPv6 addresses (`::ffff:127.x.x.x`)
- Mobile platforms now enforce TLS for all non-loopback gateway connections
- Added dangerous tool deny list for gateway HTTP `/tools/invoke` endpoint with startup validation
- Webhook extensions now properly handle proxy IP normalization and CIDR matching
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk - it strengthens security posture by preventing scope escalation attacks while maintaining backward compatibility for legitimate use cases.
- The security hardening is well-designed with comprehensive test coverage across all affected surfaces (gateway websocket, mobile platforms, webhooks, Control UI). The change from silent scope clearing to explicit rejection improves security transparency. Break-glass environment variables provide escape hatches for legitimate edge cases. Path traversal fixes and TLS enforcement on mobile platforms close additional attack vectors. All dangerous behavior now requires explicit opt-in with clear warnings.
- No files require special attention - the implementation is thorough and well-tested
<sub>Last reviewed commit: 597be3d</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20089: fix(gateway): preserve control-ui scopes when dangerouslyDisableDev...
by vashkartik · 2026-02-18
87.3%
#23364: Gateway: add risk-ack interlock for dangerous Control UI flags
by bmendonca3 · 2026-02-22
85.7%
#17605: fix: preserve scopes when disableControlUiDeviceAuth is enabled
by MisterGuy420 · 2026-02-16
84.7%
#23355: Gateway: fail closed on untrusted proxy headers
by bmendonca3 · 2026-02-22
84.1%
#12802: fix(gateway): default unscoped operator connections to read-only
by yubrew · 2026-02-09
82.9%
#17572: fix: make dangerouslyDisableDeviceAuth bypass device identity checks
by gitwithuli · 2026-02-15
82.5%
#23277: fix(gateway): preserve scopes for localhost token-auth without devi...
by dashed · 2026-02-22
81.2%
#21100: Security/Gateway: require explicit break-glass env for Control UI b...
by bmendonca3 · 2026-02-19
81.1%
#22381: Security/Gateway: block cross-origin silent auto-pairing in auth mo...
by bmendonca3 · 2026-02-21
80.9%
#22365: fix(gateway): auto-approve loopback scope upgrades
by AIflow-Labs · 2026-02-21
80.8%