Skip to content

fix(exec): detect cmd wrapper carriers#62439

Merged
ngutman merged 3 commits into
mainfrom
fix/system-run-cmd-wrapper-detection
Apr 7, 2026
Merged

fix(exec): detect cmd wrapper carriers#62439
ngutman merged 3 commits into
mainfrom
fix/system-run-cmd-wrapper-detection

Conversation

@ngutman

@ngutman ngutman commented Apr 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Problem: Windows allowlist mode classified cmd.exe transport wrappers from the original argv only, so transparent carriers like env cmd.exe /c ... were missed.
  • Why it matters: that could bypass the intended approval gate for cmd.exe /c when the inner payload was otherwise allowlisted.
  • What changed: system.run now checks cmd.exe wrapper transport after unwrapping transparent dispatch wrappers, and adds a regression test for env cmd.exe /c ....
  • What did NOT change (scope boundary): POSIX transport-wrapper behavior, command-text binding, and the allowlist policy model itself.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: cmdInvocation in src/node-host/invoke-system-run.ts only inspected the original request argv, while shell-wrapper parsing already unwraps transparent dispatch wrappers before identifying cmd.exe /c.
  • Missing detection / guardrail: there was no regression test covering Windows cmd.exe wrappers behind a transparent carrier like env.
  • Contributing context (if known): fix(exec): align node shell allowlist wrappers #62401 intentionally kept cmd.exe /c wrappers approval-gated, but the transport classification path and shell-wrapper parsing path drifted.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/node-host/invoke-system-run.test.ts
  • Scenario the test should lock in: env cmd.exe /d /s /c <allowlisted script> still requires approval in Windows allowlist mode.
  • Why this is the smallest reliable guardrail: the regression only appears after request parsing, allowlist analysis, and policy classification interact.
  • Existing test that already covers this (if any): direct cmd.exe /c ... approval gating was already covered.
  • If no new test is added, why not:

User-visible / Behavior Changes

  • Windows allowlist-mode system.run requests wrapped by transparent carriers like env now keep the intended cmd.exe /c approval gate.

Diagram (if applicable)

Before:
[env cmd.exe /c allowlisted-script] -> [shell parser unwraps to cmd.exe] -> [cmd detection still sees env] -> [approval gate skipped]

After:
[env cmd.exe /c allowlisted-script] -> [dispatch unwrap for cmd detection] -> [cmd.exe classified correctly] -> [approval required]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (Yes)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: this narrows execution by restoring the intended approval requirement for a Windows wrapper edge case; regression coverage locks it in.

Repro + Verification

Environment

  • OS: macOS host with Windows platform mocked in tests
  • Runtime/container: Node 22+/pnpm test harness
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): tools.exec.security=allowlist, tools.exec.ask=on-miss

Steps

  1. Allowlist a Windows .cmd script path.
  2. Invoke system.run with env cmd.exe /d /s /c <script> --limit 5 in allowlist mode.
  3. Observe the policy decision without explicit approval.

Expected

  • The request is denied pending approval.

Actual

  • Before this fix, the transport could be misclassified as non-cmd.exe and auto-run.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: direct cmd.exe /c ... gating still passes, and the new env cmd.exe /c ... regression test now denies execution without approval.
  • Edge cases checked: transparent dispatch-wrapper unwrapping for cmd.exe detection only; no POSIX transport-wrapper assertions changed.
  • What you did not verify: full repo-wide pnpm check on the pre-fix commit path, since an unrelated tsgo failure on current main blocked the first commit attempt; reran the touched-surface tests after rebasing onto latest origin/main.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: wrapper unwrapping for cmd.exe detection could over-classify non-transparent carriers.
    • Mitigation: the fix reuses the existing transparent dispatch-wrapper unwrapping helper already used by shell-wrapper parsing, and adds regression coverage for the targeted case.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels Apr 7, 2026
@greptile-apps

greptile-apps Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR fixes a Windows allowlist bypass where cmd.exe wrapped inside a transparent dispatch carrier like env (e.g. env cmd.exe /d /s /c ...) was not detected as a cmd.exe invocation, allowing the approval gate to be skipped. The one-line fix in invoke-system-run.ts calls unwrapDispatchWrappersForResolution on the request argv before passing it to isCmdExeInvocation, mirroring how the shell-wrapper parser already sees the inner command. A regression test is added that verifies both the unwrapped argv is passed to isCmdExeInvocation and that the invocation is correctly denied without approval.

Confidence Score: 5/5

Safe to merge; the fix narrows execution by restoring the intended cmd.exe approval gate for transparent carrier wrappers.

The change is minimal and correct: unwrapDispatchWrappersForResolution already peels transparent wrappers with no modifiers and returns the inner argv, so a plain env cmd.exe /c ... now correctly surfaces cmd.exe as the first token. The regression test verifies the unwrapped argv is passed to isCmdExeInvocation and that execution is denied. Existing direct cmd.exe /c coverage is unaffected. No P0 or P1 issues found.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(exec): detect cmd wrapper carriers" | Re-trigger Greptile

@ngutman ngutman self-assigned this Apr 7, 2026
@ngutman
ngutman force-pushed the fix/system-run-cmd-wrapper-detection branch from 8aa85ba to 4b4ed34 Compare April 7, 2026 11:26
@ngutman
ngutman merged commit de6bac3 into main Apr 7, 2026
7 checks passed
@ngutman
ngutman deleted the fix/system-run-cmd-wrapper-detection branch April 7, 2026 11:27
@ngutman

ngutman commented Apr 7, 2026

Copy link
Copy Markdown
Member Author

Landed via temp rebase onto main.

  • Full gate attempted: pnpm lint && pnpm build && pnpm test
  • Local gate result: pnpm lint and pnpm build passed; pnpm test hit unrelated existing latest-main failures while latest origin/main also had failing check / check-additional runs.
  • Scoped verification: pnpm test src/infra/exec-wrapper-resolution.test.ts src/node-host/invoke-system-run.test.ts src/node-host/exec-policy.test.ts src/infra/system-run-command.test.ts
  • Land commit: PLACEHOLDER_LAND_SHA
  • Merge commit: PLACEHOLDER_MERGE_SHA

Thanks @ngutman!

@ngutman

ngutman commented Apr 7, 2026

Copy link
Copy Markdown
Member Author

Follow-up with concrete SHAs:

lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
* fix(exec): detect cmd wrapper carriers

* fix(exec): block env cmd wrapper carriers

* fix: keep cmd wrapper carriers approval-gated (openclaw#62439) (thanks @ngutman)
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
* fix(exec): detect cmd wrapper carriers

* fix(exec): block env cmd wrapper carriers

* fix: keep cmd wrapper carriers approval-gated (openclaw#62439) (thanks @ngutman)
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
* fix(exec): detect cmd wrapper carriers

* fix(exec): block env cmd wrapper carriers

* fix: keep cmd wrapper carriers approval-gated (openclaw#62439) (thanks @ngutman)
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
* fix(exec): detect cmd wrapper carriers

* fix(exec): block env cmd wrapper carriers

* fix: keep cmd wrapper carriers approval-gated (openclaw#62439) (thanks @ngutman)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant