OpenClaw < 2026.3.7 - Shell Approval Gating Bypass via Dispatch Wrapper Depth Mismatch
Description
OpenClaw versions prior to 2026.3.7 contain a shell approval gating bypass vulnerability in system.run dispatch-wrapper handling that allows attackers to skip shell wrapper approval requirements. The approval classifier and execution planner apply different depth-boundary rules, permitting exactly four transparent dispatch wrappers like repeated env invocations before /bin/sh -c to bypass security=allowlist approval gating by misaligning classification with execution planning.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.7 | 2026.3.7 |
Affected products
1Patches
12fc95a7cfc1efix(exec): close dispatch-wrapper boundary drift
4 files changed · +58 −2
CHANGELOG.md+1 −0 modified@@ -305,6 +305,7 @@ Docs: https://docs.openclaw.ai - Media/mime unknown-kind handling: return `undefined` (not `"unknown"`) for missing/unrecognized MIME kinds and use document-size fallback caps for unknown remote media, preventing phantom `<media:unknown>` Signal events from being treated as real messages. (#39199) Thanks @nicolasgrasset. - Nodes/system.run allow-always persistence: honor shell comment semantics during allowlist analysis so `#`-tailed payloads that never execute are not persisted as trusted follow-up commands. Thanks @tdjackey for reporting. - Signal/inbound attachment fan-in: forward all successfully fetched inbound attachments through `MediaPaths`/`MediaUrls`/`MediaTypes` (instead of only the first), and improve multi-attachment placeholder summaries in mention-gated pending history. (#39212) Thanks @joeykrug. +- Nodes/system.run dispatch-wrapper boundary: keep shell-wrapper approval classification active at the depth boundary so `env` wrapper stacks cannot reach `/bin/sh -c` execution without the expected approval gate. Thanks @tdjackey for reporting. ## 2026.3.2
src/infra/exec-wrapper-resolution.ts+6 −2 modified@@ -509,7 +509,9 @@ function hasEnvManipulationBeforeShellWrapperInternal( depth: number, envManipulationSeen: boolean, ): boolean { - if (depth >= MAX_DISPATCH_WRAPPER_DEPTH) { + // The wrapper found exactly at the configured dispatch depth boundary still needs + // to participate in approval classification; only paths beyond that boundary fail closed. + if (depth > MAX_DISPATCH_WRAPPER_DEPTH) { return false; } @@ -607,7 +609,9 @@ function extractShellWrapperCommandInternal( rawCommand: string | null, depth: number, ): ShellWrapperCommand { - if (depth >= MAX_DISPATCH_WRAPPER_DEPTH) { + // The shell wrapper reached at the boundary depth is still semantically relevant. + // Only deeper wrapper stacks should be dropped as overflow. + if (depth > MAX_DISPATCH_WRAPPER_DEPTH) { return { isWrapper: false, command: null }; }
src/infra/system-run-command.test.ts+11 −0 modified@@ -54,6 +54,17 @@ describe("system run command helpers", () => { "echo hi", ]), ).toBe("echo hi"); + expect( + extractShellCommandFromArgv([ + "/usr/bin/env", + "/usr/bin/env", + "/usr/bin/env", + "/usr/bin/env", + "/bin/sh", + "-c", + "echo hi", + ]), + ).toBe("echo hi"); }); test("extractShellCommandFromArgv supports fish and pwsh wrappers", () => {
src/node-host/invoke-system-run.test.ts+40 −0 modified@@ -858,6 +858,46 @@ describe("handleSystemRunInvoke mac app exec host routing", () => { expectApprovalRequiredDenied({ sendNodeEvent, sendInvokeResult }); }); + it("denies env-wrapped shell payloads at the dispatch depth boundary", async () => { + if (process.platform === "win32") { + return; + } + const { runCommand, sendInvokeResult, sendNodeEvent } = createInvokeSpies({ + runCommand: vi.fn(async () => { + throw new Error("runCommand should not be called for depth-boundary shell wrappers"); + }), + }); + + await withTempApprovalsHome({ + approvals: createAllowlistOnMissApprovals({ + agents: { + main: { + allowlist: [{ pattern: "/usr/bin/env" }], + }, + }, + }), + run: async ({ tempHome }) => { + const marker = path.join(tempHome, "depth4-pwned.txt"); + await runSystemInvoke({ + preferMacAppExecHost: false, + command: buildNestedEnvShellCommand({ + depth: 4, + payload: `echo PWNED > ${marker}`, + }), + security: "allowlist", + ask: "on-miss", + runCommand, + sendInvokeResult, + sendNodeEvent, + }); + expect(fs.existsSync(marker)).toBe(false); + }, + }); + + expect(runCommand).not.toHaveBeenCalled(); + expectApprovalRequiredDenied({ sendNodeEvent, sendInvokeResult }); + }); + it("denies nested env shell payloads when wrapper depth is exceeded", async () => { if (process.platform === "win32") { return;
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
6- github.com/openclaw/openclaw/commit/2fc95a7cfc1eb9306356510b0251b6d51fb1c0b0ghsapatchWEB
- github.com/advisories/GHSA-r6qf-8968-wj9qghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-r6qf-8968-wj9qghsathird-party-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-27183ghsaADVISORY
- vulncheck.com/advisories/openclaw-mar-shell-approval-gating-bypass-via-dispatch-wrapper-depth-mismatchghsathird-party-advisoryWEB
- github.com/openclaw/openclaw/releases/tag/v2026.3.7ghsaWEB
News mentions
0No linked articles in our index yet.