fix(gateway): allow silent role upgrades for local loopback clients#59092
Conversation
Greptile SummaryThis PR fixes a regression from v2026.3.31 that broke Key changes:
Confidence Score: 5/5Safe to merge — minimal, targeted changes with strong test coverage and no logic errors found. Both fixes are logically correct. The No files require special attention. Reviews (2): Last reviewed commit: "fix(gateway ): allow silent role upgrade..." | Re-trigger Greptile |
2682a7b to
57460f1
Compare
|
Quick context on the approach and a follow-up on the greptile feedback. The core issue is a JS truthy trap — I deliberately kept the fix narrow: the Regarding @greptile-apps's review — good catch on the Ready for review. |
When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045
57460f1 to
e2d08e5
Compare
obviyus
left a comment
There was a problem hiding this comment.
Reviewed latest changes; landing now.
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…penperf) * fix(gateway ): allow silent role upgrades for local loopback clients When a local loopback client connects with a role not covered by existing device tokens, listEffectivePairedDeviceRoles incorrectly returns an empty role set for devices whose tokens map is an empty object. This triggers a role-upgrade pairing request that shouldAllowSilentLocalPairing rejects because it does not recognise the role-upgrade reason. Fix listEffectivePairedDeviceRoles to fall back to legacy role fields when the tokens map has no entries, and extend shouldAllowSilentLocalPairing to accept role-upgrade for local clients. Fixes openclaw#59045 * fix: restore local loopback role upgrades (openclaw#59092) (thanks @openperf) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
exectool connections on local loopback setups (Issue [Bug]: v2026.3.31 breaks exec on local loopback setups #59045). When a local client connects with a new role (e.g.,nodehost connecting after anoperatorclient), the connection is rejected with a 1008 "pairing required" error, even though the device is already paired.listEffectivePairedDeviceRoles(insrc/infra/device-pairing.ts, line 169) incorrectly returns an empty array[]whendevice.tokensis an empty object{}, failing to fall back to the legacydevice.rolesfield. This happens because the original guardif (device.tokens)is truthy for{}, butlistActiveTokenRoles({})returnsundefined(sinceObject.values({})yields no entries), resulting inundefined ?? []=[].shouldAllowSilentLocalPairing(insrc/gateway/server/ws-connection/handshake-auth-helpers.ts, line 58) does not support therole-upgradereason. This prevents local loopback clients from automatically approving the role upgrade via silent pairing, causing a 1008 close.listEffectivePairedDeviceRolesto fall back to legacy role fields (device.roles,device.role) when thetokensmap is present but has no entries (empty object{}). When token entries exist but are all revoked, the function still returns[]to preserve the security invariant that revoked tokens permanently remove role access.shouldAllowSilentLocalPairingto explicitly allow therole-upgradereason for local clients, ensuring that loopback connections can seamlessly acquire new roles without manual intervention.src/infra/device-pairing.ts: FixedlistEffectivePairedDeviceRolesfallback logic to distinguish empty tokens map from revoked tokens.src/infra/device-pairing.test.ts: Added regression test for the empty tokens map fallback scenario.src/gateway/server/ws-connection/handshake-auth-helpers.ts: Addedrole-upgradeto the allowed reasons inshouldAllowSilentLocalPairing.src/gateway/server/ws-connection/handshake-auth-helpers.test.ts: Updated existing test and added new test for silent role-upgrade boundary (local vs remote).reason === "scope-upgrade"is force-set tosilent: falseinrequirePairing) is untouched.Reproduction
mainorv2026.3.31.operatorrole to establish initial pairing.exectool or connect anodehost from the same local loopback address.Risk / Mitigation
isLocalClientguard, which restricts this behavior strictly to loopback connections (127.0.0.1 / ::1). Remote connections still require explicit approval for role upgrades. Additionally, the fix inlistEffectivePairedDeviceRolespreserves the security invariant that explicitly revoked tokens cannot be bypassed — only empty token maps (from fresh or legacy pairing records) trigger the fallback. Comprehensive unit tests have been added to verify both the positive path (local role-upgrade succeeds) and the negative boundary (remote role-upgrade is rejected; revoked tokens remain ineffective).Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Fixes #59045