From 6e4ab036c76f0d94226db56c0edf70017fae2ba6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 20 Mar 2026 13:04:55 +0000 Subject: [PATCH] fix(gateway): preserve trusted-proxy scopes on device-less allow path The previous condition cleared scopes for all device-less non-control-ui connections because !isControlUi made the OR always true. Split logic: clear on non-allow; normalize to operator.read only for allow + token/password. --- .../server/ws-connection/message-handler.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index 41d8234f45b..3abd47dde54 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -538,15 +538,19 @@ export function attachGatewayWsMessageHandler(params: { // Shared token/password auth can bypass pairing for trusted operators, but // device-less backend clients must not self-declare scopes. Control UI // keeps its explicitly allowed device-less scopes on the allow path. - // When allowing device-less token auth, grant operator.read so read RPCs work. - // Restrict read-scope fallback to token/password auth only; trusted-proxy - // sessions must not gain read scope without a bound device identity. + // When allowing device-less token/password auth, normalize to operator.read + // so read RPCs work. Do not auto-grant read for trusted-proxy (proxy proves + // identity but must not upgrade scope); preserve explicit scopes there. const grantReadForTokenAuth = decision.kind === "allow" && sharedAuthOk && (authMethod === "token" || authMethod === "password"); - if (!device && (!isControlUi || decision.kind !== "allow" || trustedProxyAuthOk)) { - clearUnboundScopes(grantReadForTokenAuth); + if (!device) { + if (decision.kind !== "allow") { + clearUnboundScopes(false); + } else if (!isControlUi && grantReadForTokenAuth) { + clearUnboundScopes(true); + } } if (decision.kind === "allow") { return true;