openclaw - ✅(Solved) Fix [Bug]: Skill-invoked exec commands silently dropped with security: allowlist + ask: deny — resolved binary is /bin/sh, not the target CLI [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#57377Fetched 2026-04-08 01:50:28
View on GitHub
Comments
0
Participants
1
Timeline
14
Reactions
0
Participants
Timeline (top)
referenced ×7labeled ×3cross-referenced ×2closed ×1

When a skill (e.g., the gog skill from ClawHub) invokes a CLI tool via the exec tool, OpenClaw constructs a compound shell command:

cat ~/.local/share/mise/.../openclaw/skills/gog/SKILL.md && printf '\n---CMD---\n' && gog-wrapper calendar events primary --today --json
  • Skill: gog (ClawHub steipete/gog, manually placed in ~/.openclaw/skills/gog/)

This compound command is routed through /bin/sh -c "...". With exec approvals configured as security: "allowlist" and ask: "deny", the approval system resolves the binary as /bin/sh, fails to match it against the allowlist (which contains /opt/homebrew/bin/gog), and silently drops the execution. No error is returned to the agent, no log entry is emitted, and no approval prompt is sent.

The agent receives a null/empty tool result, which causes model-dependent failure modes:

  • Claude Sonnet 4.6 / Opus 4.6: Enters an infinite </s> token streaming loop, consuming tokens until manually aborted
  • Claude Haiku 4.5: Fabricates plausible-looking tool output (hallucinated emails, calendar events, JSON responses)
  • OpenAI GPT-5.4: Handles the void gracefully (reports "no output" or retries once)

Error Message

This compound command is routed through /bin/sh -c "...". With exec approvals configured as security: "allowlist" and ask: "deny", the approval system resolves the binary as /bin/sh, fails to match it against the allowlist (which contains /opt/homebrew/bin/gog), and silently drops the execution. No error is returned to the agent, no log entry is emitted, and no approval prompt is sent. 5. Observe: no output returned, no error logged, no approval prompt. The agent either loops, hallucinates, or goes silent depending on the model.

  • (c) The silent drop should at minimum return an error to the agent (e.g., "exec denied: allowlist miss for /bin/sh") so the model can report the failure instead of looping/hallucinating
  • No error returned to agent

Root Cause

Additional context

The skill invocation pipeline constructs compound commands (cat SKILL.md && command) that are inherently incompatible with binary-path-only allowlist matching. This affects any skill that wraps a CLI tool, not just gog. The silent drop failure mode is particularly dangerous because different models handle the void differently — with Anthropic models entering token-consuming loops that can run up significant costs before the user notices.

Fix Action

Fix / Workaround

Workaround config:

{
  "host": "gateway", 
  "security": "full",       ← bypasses approval system entirely
  "ask": "off",
  "pathPrepend": ["/opt/homebrew/bin", "/Users/openclaw/.openclaw/bin"]
}

Workarounds found

PR fix notes

PR #57584: fix: evaluate shell wrapper inline compound commands against allowlist (#57377)

Description (problem / solution / changelog)

Summary

  • Problem: When a skill invokes a CLI tool via exec, OpenClaw constructs a compound shell command (/bin/sh -c "cat SKILL.md && printf '---CMD---' && gog-wrapper calendar events"). With security: "allowlist" + ask: "deny", the allowlist check resolves the binary as /bin/sh and silently drops the execution — no error, no log, no approval prompt.
  • Why it matters: Silent drops cause model-dependent failure modes: Claude enters infinite </s> token loops (cost risk), Haiku fabricates output (hallucination risk), only GPT handles it gracefully. This affects any skill wrapping a CLI tool.
  • What changed: Added recursive evaluation of shell wrapper inline commands — when a /bin/sh -c "..." compound command contains chain operators (&&, ||, ;), each sub-command is parsed and individually checked against the allowlist.
  • What did NOT change: Single-command shell wrappers (e.g. bash -lc 'script.sh') keep original behavior to preserve allow-always persisted-pattern security constraints. No changes to the approval prompt flow or error reporting (the silent-drop-to-error improvement is a separate concern).

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

  • Closes #57377
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: evaluateSegments() in exec-approvals-allowlist.ts only checked the outermost binary (/bin/sh) against the allowlist when processing shell wrapper segments. The extracted inline command was only used for script path and $0 "$@" carrier checks, not for recursive sub-command validation.
  • Missing detection / guardrail: No test coverage for compound commands wrapped in shell invocations.
  • Prior context: The shell wrapper resolution code (shell-wrapper-resolution.ts) already extracted inline commands via extractShellWrapperInlineCommand(), but the allowlist evaluator didn't use this for recursive checking.
  • Why this regressed now: This behavior likely existed since the allowlist feature was introduced — it became visible as more skills started wrapping CLI tools.

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/infra/exec-approvals-analysis.test.ts
  • Scenario the test should lock in: Compound commands inside shell wrappers are recursively evaluated; all sub-commands must be in allowlist; single-command wrappers are NOT recursively evaluated.
  • Why this is the smallest reliable guardrail: Tests directly exercise the evaluateShellAllowlist function with compound command inputs.
  • If no new test is added, why not: 5 new tests added.

User-visible / Behavior Changes

  • Skill-invoked compound shell commands (e.g. cat SKILL.md && gog-wrapper calendar events) now correctly evaluate each sub-command against the allowlist instead of checking only /bin/sh.
  • Single-command shell wrappers retain original behavior (no recursive evaluation).

Diagram (if applicable)

Before:
[skill exec] -> sh -c "cat SKILL.md && gog" -> resolves to /bin/sh -> NOT in allowlist -> SILENT DROP

After:
[skill exec] -> sh -c "cat SKILL.md && gog" -> detects chain operators -> recurse:
                                                  ├─ cat SKILL.md   -> safe bin ✓
                                                  └─ gog            -> in allowlist ✓ -> ALLOWED

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? Yes — compound shell commands are now recursively evaluated instead of being silently rejected.
    • Risk: Recursive evaluation could theoretically allow a carefully crafted compound command to pass allowlist checks.
    • Mitigation: Recursion depth limited to MAX_SHELL_WRAPPER_INLINE_EVAL_DEPTH = 3; only triggers on chain operators (&&, ||, ;); ALL sub-commands must individually satisfy the allowlist; single-command wrappers are excluded to preserve allow-always security constraints.
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS 14.x
  • Runtime/container: Node.js v25.4.0
  • Model/provider: Any (affects all models differently)
  • Relevant config: security: "allowlist", ask: "deny" in exec config

Steps

  1. Install a skill that wraps a CLI tool (e.g. gog skill)
  2. Configure security: "allowlist" with the CLI tool path
  3. Trigger the skill — it constructs sh -c "cat SKILL.md && gog-wrapper calendar events"
  4. Observe the exec result

Expected

  • Command executes successfully, returning CLI output.

Actual (before fix)

  • Command silently dropped. Agent receives empty result. Model-dependent failures ensue.

Evidence

  • Failing test/log before + passing after
  • 199/199 exec-approvals tests pass (5 new + 194 existing across 8 test files)

Human Verification (required)

  • Verified scenarios: Compound command with all binaries in allowlist passes; compound with unlisted binary fails; mixed allowlist + safe bins; single-command wrapper not recursed.
  • Edge cases checked: Recursion depth limit; Windows platform excluded; empty chain parts; single-command wrappers preserve allow-always constraints.
  • What you did not verify: End-to-end skill invocation with real CLI tool.

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.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: Recursive evaluation adds complexity to the security-critical allowlist path.
    • Mitigation: Depth limit of 3; only triggered by chain operators; comprehensive test coverage; single-command exclusion preserves existing security invariants.

Changed files

  • src/agents/pi-embedded-subscribe.handlers.messages.ts (modified, +22/-2)
  • src/infra/exec-approvals-allowlist.ts (modified, +97/-0)
  • src/infra/exec-approvals-analysis.test.ts (modified, +134/-1)

Code Example

cat ~/.local/share/mise/.../openclaw/skills/gog/SKILL.md && printf '\n---CMD---\n' && gog-wrapper calendar events primary --today --json

---

{
  "agents": {
    "main": {
      "security": "allowlist",
      "ask": "deny",
      "askFallback": "deny",
      "allowlist": [
        { "pattern": "/opt/homebrew/bin/gog" }
      ]
    }
  }
}

---

{
  "host": "gateway",
  "security": "allowlist",  ← triggers the bug
  "ask": "deny",            ← causes silent drop instead of prompt
  "pathPrepend": ["/opt/homebrew/bin"]
}

---

{
  "host": "gateway", 
  "security": "full",       ← bypasses approval system entirely
  "ask": "off",
  "pathPrepend": ["/opt/homebrew/bin", "/Users/openclaw/.openclaw/bin"]
}

---
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Summary

When a skill (e.g., the gog skill from ClawHub) invokes a CLI tool via the exec tool, OpenClaw constructs a compound shell command:

cat ~/.local/share/mise/.../openclaw/skills/gog/SKILL.md && printf '\n---CMD---\n' && gog-wrapper calendar events primary --today --json
  • Skill: gog (ClawHub steipete/gog, manually placed in ~/.openclaw/skills/gog/)

This compound command is routed through /bin/sh -c "...". With exec approvals configured as security: "allowlist" and ask: "deny", the approval system resolves the binary as /bin/sh, fails to match it against the allowlist (which contains /opt/homebrew/bin/gog), and silently drops the execution. No error is returned to the agent, no log entry is emitted, and no approval prompt is sent.

The agent receives a null/empty tool result, which causes model-dependent failure modes:

  • Claude Sonnet 4.6 / Opus 4.6: Enters an infinite </s> token streaming loop, consuming tokens until manually aborted
  • Claude Haiku 4.5: Fabricates plausible-looking tool output (hallucinated emails, calendar events, JSON responses)
  • OpenAI GPT-5.4: Handles the void gracefully (reports "no output" or retries once)

Additional context

The skill invocation pipeline constructs compound commands (cat SKILL.md && command) that are inherently incompatible with binary-path-only allowlist matching. This affects any skill that wraps a CLI tool, not just gog. The silent drop failure mode is particularly dangerous because different models handle the void differently — with Anthropic models entering token-consuming loops that can run up significant costs before the user notices.

A related but separate issue: environment variables configured in the env section of openclaw.json do not reliably propagate to exec subprocesses on 2026.3.28 (see #31583), requiring wrapper scripts to source env files explicitly.

Related issues

  • #27299 — exec tool not delivered to agent despite being in allow list (gog skill)
  • #15544 — Approval prompts fail silently, blocking agent tool usage
  • #25082 — exec allowlist wildcard pattern stored but never matches at runtime
  • #41140 — Feature request for command-content deny patterns
  • #45963 — exec bypasses approval flow when tools.exec.host is unset
  • #31583 — exec tool does not inherit skills.entries.*.env environment variables

Steps to reproduce

Steps to reproduce

  1. Install a skill that wraps a CLI tool (e.g., gog for Google Workspace)
  2. Configure exec approvals:
{
  "agents": {
    "main": {
      "security": "allowlist",
      "ask": "deny",
      "askFallback": "deny",
      "allowlist": [
        { "pattern": "/opt/homebrew/bin/gog" }
      ]
    }
  }
}
  1. Configure tools.exec.host: "gateway"
  2. Ask the agent a question that triggers the skill (e.g., "What's on my calendar today?")
  3. Observe: no output returned, no error logged, no approval prompt. The agent either loops, hallucinates, or goes silent depending on the model.

Expected behavior

Expected behavior

Either:

  • (a) The approval system should resolve the target CLI binary from the compound command, not /bin/sh, and match it against the allowlist
  • (b) The approval system should send an approval prompt when ask: "on-miss" is configured (this also fails silently in some cases — see related issue)
  • (c) The silent drop should at minimum return an error to the agent (e.g., "exec denied: allowlist miss for /bin/sh") so the model can report the failure instead of looping/hallucinating

Actual behavior

Actual behavior

  • Command is silently dropped
  • No error returned to agent
  • No log entry for the denial
  • Agent receives empty/null tool result
  • Cascading model failures (streaming loops, hallucination, or silence)

OpenClaw version

2026.3.28 (f9b1079)

Operating system

macOS 26.3.1 (arm64) — Mac Mini M4

Install method

curl -fsSL https://openclaw.ai/install.sh | bash

Model

Claude Sonnet 4.6 / Claude Opus 4.6 / Claude Haiku 4.5 / OpenAI GPT-5.4

Provider / routing chain

Telegram → OpenClaw Gateway (loopback:18789, token auth) → exec tool → /bin/sh -c "cat SKILL.md && gog-wrapper ..." → silent drop at approval layer

Additional provider/model setup details

Gateway: loopback bind, token auth, tools.exec.host=gateway Global tool profile: minimal Main agent tool profile: minimal + allow [exec, read, web_search, web_fetch, sessions_spawn, sessions_list, sessions_history, agents_list, process]

Models tested against this bug:

  • anthropic/claude-sonnet-4-6 (primary at time of discovery) → </s> streaming loop
  • anthropic/claude-opus-4-6 → </s> streaming loop
  • anthropic/claude-haiku-4-5 → hallucinated tool output
  • openai/gpt-5.4 (current primary) → handles void gracefully

Auth profiles: Anthropic (api_key via env ref), OpenAI (token via env ref) Per-agent exec approvals: main=allowlist [/opt/homebrew/bin/gog], builder=allowlist [node/npm/npx/git], reviewer=deny-all Skill: gog (manual SKILL.md in ~/.openclaw/skills/gog/, not ClawHub CLI installed) Env injection: GOG_KEYRING_PASSWORD and GOG_ACCOUNT set in openclaw.json env section (confirmed present in gateway process, not reaching exec subprocess)

tools.exec config at time of bug:

{
  "host": "gateway",
  "security": "allowlist",  ← triggers the bug
  "ask": "deny",            ← causes silent drop instead of prompt
  "pathPrepend": ["/opt/homebrew/bin"]
}

Workaround config:

{
  "host": "gateway", 
  "security": "full",       ← bypasses approval system entirely
  "ask": "off",
  "pathPrepend": ["/opt/homebrew/bin", "/Users/openclaw/.openclaw/bin"]
}

Logs, screenshots, and evidence

Impact and severity

No response

Additional information

Workarounds found

  1. tools.exec.security: "full" + tools.exec.ask: "off" — bypasses the approval system entirely. Works but removes all exec guardrails.
  2. Wrapper scripts — Create a wrapper (e.g., gog-wrapper) that bundles environment variables and calls the target binary via exec. Add the wrapper path to the allowlist. Combined with autoAllowSkills: true, skill-invoked commands get auto-approved.
  3. Adding /bin/sh and /bin/zsh to the allowlist — partially works but defeats the purpose of allowlisting specific binaries.

extent analysis

Fix Plan

To address the issue of the approval system silently dropping the execution of compound commands, we need to modify the approval system to resolve the target CLI binary from the compound command. Here are the steps:

  • Modify the approval system: Update the approval system to parse the compound command and extract the target CLI binary. This can be done by splitting the command string and checking if it starts with a shell executable (e.g., /bin/sh or /bin/zsh).
  • Update the allowlist matching: Modify the allowlist matching!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!logic to check if the extracted target CLI binary is in the allowlist.
  • **Return an error on allowlist!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Either:

  • (a) The approval system should resolve the target CLI binary from the compound command, not /bin/sh, and match it against the allowlist
  • (b) The approval system should send an approval prompt when ask: "on-miss" is configured (this also fails silently in some cases — see related issue)
  • (c) The silent drop should at minimum return an error to the agent (e.g., "exec denied: allowlist miss for /bin/sh") so the model can report the failure instead of looping/hallucinating

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING