Skip to content

[Bug]: Exec approval checks use symlink path but execution uses real path #45595

Description

@jasonftl

Bug type

Behavior bug (incorrect output/state without crash)

Summary

The exec approval system checks resolvedPath (may be a symlink) but execution pins to resolvedRealPath (the real file). The system computes both paths but uses different ones for trust vs execution - so it can approve or reject one path and then run a different one.

Steps to reproduce

  1. Have a binary available through a symlink on PATH (Homebrew, nix, asdf all do this):

    /opt/homebrew/bin/rg  ->  /opt/homebrew/Cellar/ripgrep/14.1.1/bin/rg
    
  2. Add an allowlist entry for the real file:

    {"pattern": "/opt/homebrew/Cellar/ripgrep/14.1.1/bin/rg"}
  3. The agent runs rg -n TODO.

Expected behavior

The command is auto-approved. The binary at the end of the symlink chain is allowlisted.

Actual behavior

The command is blocked. Approval matching compares against /opt/homebrew/bin/rg (symlink) while execution later pins to /opt/homebrew/Cellar/ripgrep/14.1.1/bin/rg (real file). They do not match.

The reverse is also wrong: allowlist the symlink path, change the symlink target, and the old approval still holds while execution runs the new binary.

OpenClaw version

2026.3.12

Operating system

All platforms (scope varies - see additional information)

Install method

Any

Model

N/A (exec approval layer, not model-dependent)

Provider / routing chain

N/A (exec approval layer, not provider-dependent)

Config file / key location

tools.exec.allowlist, tools.exec.safeBins, tools.exec.safeBinTrustedDirs, autoAllowSkills in exec-approvals.json

Additional provider/model setup details

No response

Logs, screenshots, and evidence

No response

Impact and severity

  • Affected: Any user whose commands resolve through symlinks. This is the default on macOS with Homebrew and common on Linux with nix, asdf, etc.
  • Severity: Medium. The allowlist-miss blocks agents unnecessarily. The approval-drift is a security concern with no current mitigation on the executable path.
  • Frequency: Affects any symlinked binary. On a typical macOS Homebrew system, most binaries in /opt/homebrew/bin/ are symlinks.

Additional information

Root cause:

  1. resolveExecutablePath() returns the executable path as found. It does not resolve symlinks.
  2. buildCommandResolution() stores both resolvedPath and resolvedRealPath.
  3. Allowlist and related trust checks mostly use resolvedPath.
  4. Execution uses resolvedRealPath ?? resolvedPath.

The system already knows both values, but does not use one canonical executable identity across trust checks, persistence, and execution.

This is not only one matcher bug. The same path mismatch shows up in:

  • allowlist matching
  • safe-bin trust checks
  • skill auto-allow
  • "Allow Always" persistence
  • approval metadata / last-used path recording
  • openclaw doctor safe-bin hints (diagnostic only)

Platform notes:

  • POSIX: affected as reported. Matching generally uses the symlink-form executable path.
  • Windows: plain allowlist matching is partly protected because the matcher realpaths non-wildcard paths, but the shared persistence and skill-trust paths still use the non-canonical executable path.

Workaround:

Use the symlink path in allowlist entries, not the real file:

{"pattern": "/opt/homebrew/bin/rg"}

The "Always Allow" button also works because it saves the symlink path. The trade-off is approval drift if the symlink target changes.

Key files:

  • src/infra/executable-path.ts
  • src/infra/exec-command-resolution.ts
  • src/infra/exec-approvals-allowlist.ts
  • src/infra/exec-approvals-analysis.ts
  • src/node-host/runner.ts
  • src/node-host/invoke-system-run.ts
  • src/agents/bash-tools.exec-host-gateway.ts

Related:

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions