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.
This commit is contained in:
Cursor Agent 2026-03-20 13:04:55 +00:00
parent 4d413b78eb
commit 6e4ab036c7
No known key found for this signature in database

View File

@ -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;