High severity8.8NVD Advisory· Published Apr 28, 2026· Updated Apr 30, 2026
CVE-2026-41404
CVE-2026-41404
Description
OpenClaw before 2026.3.31 contains an incomplete scope-clearing vulnerability in trusted-proxy authentication mode that allows operator.admin privilege escalation. Attackers can exploit this by declaring operator scopes on non-Control-UI clients, allowing self-declared scopes to persist on identity-bearing authentication paths and escalate privileges.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.31 | 2026.3.31 |
Affected products
1Patches
18b88b927cb07gateway: clear unbound scopes for trusted-proxy auth (#57692)
3 files changed · +97 −4
src/gateway/server/ws-connection/connect-policy.test.ts+69 −0 modified@@ -3,6 +3,7 @@ import { evaluateMissingDeviceIdentity, isTrustedProxyControlUiOperatorAuth, resolveControlUiAuthPolicy, + shouldClearUnboundScopesForMissingDeviceIdentity, shouldSkipControlUiPairing, } from "./connect-policy.js"; @@ -300,4 +301,72 @@ describe("ws connect policy", () => { ).toBe(tc.expected); } }); + + test("clears unbound scopes for device-less shared auth outside explicit preservation cases", () => { + const nonControlUi = resolveControlUiAuthPolicy({ + isControlUi: false, + controlUiConfig: undefined, + deviceRaw: null, + }); + const controlUi = resolveControlUiAuthPolicy({ + isControlUi: true, + controlUiConfig: { allowInsecureAuth: true }, + deviceRaw: null, + }); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "allow" }, + controlUiAuthPolicy: nonControlUi, + preserveInsecureLocalControlUiScopes: false, + authMethod: "token", + }), + ).toBe(true); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "allow" }, + controlUiAuthPolicy: nonControlUi, + preserveInsecureLocalControlUiScopes: false, + authMethod: "password", + }), + ).toBe(true); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "allow" }, + controlUiAuthPolicy: nonControlUi, + preserveInsecureLocalControlUiScopes: false, + authMethod: "trusted-proxy", + }), + ).toBe(true); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "allow" }, + controlUiAuthPolicy: nonControlUi, + preserveInsecureLocalControlUiScopes: false, + authMethod: undefined, + trustedProxyAuthOk: true, + }), + ).toBe(true); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "allow" }, + controlUiAuthPolicy: controlUi, + preserveInsecureLocalControlUiScopes: true, + authMethod: "token", + }), + ).toBe(false); + + expect( + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision: { kind: "reject-device-required" }, + controlUiAuthPolicy: nonControlUi, + preserveInsecureLocalControlUiScopes: false, + authMethod: undefined, + }), + ).toBe(true); + }); });
src/gateway/server/ws-connection/connect-policy.ts+20 −0 modified@@ -81,6 +81,26 @@ export type MissingDeviceIdentityDecision = | { kind: "reject-unauthorized" } | { kind: "reject-device-required" }; +export function shouldClearUnboundScopesForMissingDeviceIdentity(params: { + decision: MissingDeviceIdentityDecision; + controlUiAuthPolicy: ControlUiAuthPolicy; + preserveInsecureLocalControlUiScopes: boolean; + authMethod: string | undefined; + trustedProxyAuthOk?: boolean; +}): boolean { + return ( + params.decision.kind !== "allow" || + (!params.controlUiAuthPolicy.allowBypass && + !params.preserveInsecureLocalControlUiScopes && + // trusted-proxy auth can bypass pairing for some clients, but those + // self-declared scopes are still unbound without device identity. + (params.authMethod === "token" || + params.authMethod === "password" || + params.authMethod === "trusted-proxy" || + params.trustedProxyAuthOk === true)) + ); +} + export function evaluateMissingDeviceIdentity(params: { hasDeviceIdentity: boolean; role: GatewayRole;
src/gateway/server/ws-connection/message-handler.ts+8 −4 modified@@ -90,6 +90,7 @@ import { evaluateMissingDeviceIdentity, isTrustedProxyControlUiOperatorAuth, resolveControlUiAuthPolicy, + shouldClearUnboundScopesForMissingDeviceIdentity, shouldSkipControlUiPairing, } from "./connect-policy.js"; import { @@ -548,10 +549,13 @@ export function attachGatewayWsMessageHandler(params: { // allow path, including trusted token-authenticated backend operators. if ( !device && - (decision.kind !== "allow" || - (!controlUiAuthPolicy.allowBypass && - !preserveInsecureLocalControlUiScopes && - (authMethod === "token" || authMethod === "password" || trustedProxyAuthOk))) + shouldClearUnboundScopesForMissingDeviceIdentity({ + decision, + controlUiAuthPolicy, + preserveInsecureLocalControlUiScopes, + authMethod, + trustedProxyAuthOk, + }) ) { clearUnboundScopes(); }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/openclaw/openclaw/commit/8b88b927cb0747ad24d95b07d35682bf85dc5b0envdPatchWEB
- github.com/advisories/GHSA-g374-mggx-p6xcghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-g374-mggx-p6xcnvdVendor AdvisoryWEB
- www.vulncheck.com/advisories/openclaw-operator-admin-privilege-escalation-via-trusted-proxy-authenticationnvdThird Party Advisory
- github.com/openclaw/openclaw/releases/tag/v2026.3.31ghsaWEB
News mentions
0No linked articles in our index yet.