The Windows node's exec-approval policy decides which commands the gateway/agent may run on the user's host. system.run is enforced by evaluating the outer command AND re-evaluating every sub-command of a chain, denying if any sub-command is denied — a model the code already relies on and tests for & / ; chains. But the chain splitter only recognises ; , & , && , || . A single pipe | and a $(...) command substitution are not treated as command boundaries, so a denied command placed behind an allowed one through a pipe or a substitution ( echo ok | del /s /q C:\… , echo hi $(Remove-Item -Recurse -Force C:\…) ) produces no sub-command target — only the allowed head is evaluated, the outer allow-glob matches, and the denied stage executes. A gateway/agent able to issue system.run thereby bypasses the allowlist and runs arbitrary commands on the host (CWE-863 / CWE-78).
Enforced path. SystemCapability registers and dispatches system.run → HandleRunAsync , which calls _approvalPolicy.Evaluate(fullCommand, shell) (the V1 ExecApprovalPolicy , wired live in NodeService ) and then, for defense-in-depth, re-evaluates each sub-command returned by ExecShellWrapperParser.Expand(fullCommand, shell) , denying on any denied sub-command. The existing test SystemRun_WithCommandChain_DeniesBlockedSegment asserts this for echo ok & del … .
The gap. ExecShellWrapperParser.SplitTopLevelCommands splits only on ; , & , && , || . A single | is appended as a literal (only || splits), and $(...) /backtick substitutions are never decomposed. For echo ok | del … , Expand returns zero targets, so the sole decision is the outer glob match on the head.
Engine amplifier. ExecApprovalPolicy.MatchesPattern compiles an allow glob echo * to ^echo\ .*$ ( * → .* , no Singleline ) and Evaluate returns on the FIRST matching rule, with all Allow rules ordered before the Deny rules. So the outer match resolves to Allow for any command whose head is an allowed prefix ( echo , Get- , ping , cat , …), and the piped/substituted tail rides along unevaluated.
PoC (benign — nothing is executed; only the classification is read)
Constructing ExecApprovalPolicy exactly as a fresh node does (no policy file ⇒ built-in default rules + DefaultAction=Deny) and calling the same method the enforced path calls ( SystemCapability.cs → _approvalPolicy.Evaluate ) returns Allow for deny-listed commands:
End-to-end through system.run : Expand("echo ok | del …") yields no sub-command target, so the outer Allow is final and the del executes (proven by the regression tests below, which fail on the current code and pass after the fix).
CWE-863 (Incorrect Authorization) / CWE-78 (OS command injection via allowlist bypass). system.run is a gateway/agent-driven node capability, and the exec-approval allowlist is the control that bounds a connected agent to a small set of read-only/diagnostic commands. Bypassing it lets a client that can issue system.run — a lower-trust or prompt-injected/compromised agent — execute arbitrary commands on the user's Windows host without an approval prompt (the bypass rides an auto-allowed prefix, so DefaultAction=Deny does not help). Confidentiality, integrity, and availability of the host are all at risk (arbitrary process launch, file deletion, download-and-run).
Suggested fix (verified: 5/5 tested bypasses closed, 0 legit journeys broken, 0 regressions; + regression tests)
Complete the sub-command decomposition the policy already relies on: treat a single | as a command separator (exactly like ; / & ), and decompose $(...) substitutions into their own evaluation targets. No change to the matcher, so paths containing ( ) $ are unaffected (no false-positives).
Plus regression tests: pipe- and substitution-chained denied stages must be blocked ( SystemRun_WithPipedChain_DeniesBlockedSegment , SystemRun_WithCommandSubstitution_DeniesBlockedInnerCommand in ExecApprovalPolicyTests.cs, mirroring the existing & -chain test; Expand_SinglePipeSeparated… , Expand_CommandSubstitution… , Expand_NestedCommandSubstitution… in ExecShellWrapperParserTests.cs), plus a no-over-deny guard ( SystemRun_WithBenignPipe_AllowsWhenEverySegmentAllowed ).
Deterministic differential (enforced system.run path, before vs after the fix; full OpenClaw.Shared.Tests suite run on Windows/.NET SDK 10):
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
