VYPR
Moderate severityNVD Advisory· Published Mar 19, 2026· Updated Mar 25, 2026

OpenClaw < 2026.2.21 - Arbitrary File Read via grep -e Flag Policy Bypass

CVE-2026-32022

Description

OpenClaw versions prior to 2026.2.21 contain a stdin-only policy bypass vulnerability in the grep tool within tools.exec.safeBins that allows attackers to read arbitrary files by supplying a pattern via the -e flag parameter. Attackers can include a positional filename operand to bypass file access restrictions and read sensitive files .env from the working directory.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
< 2026.2.212026.2.21

Affected products

1

Patches

1
c6ee14d60e4c

fix(security): block grep safe-bin file-read bypass

https://github.com/openclaw/openclawPeter SteinbergerFeb 21, 2026via ghsa
5 files changed · +45 1
  • CHANGELOG.md+1 0 modified
    @@ -202,6 +202,7 @@ Docs: https://docs.openclaw.ai
     - Security/Exec: remove file-existence oracle behavior from `tools.exec.safeBins` by using deterministic argv-only stdin-safe validation and blocking file-oriented flags (for example `sort -o`, `jq -f`, `grep -f`) so allow/deny results no longer disclose host file presence. This ships in the next npm release. Thanks @nedlir for reporting.
     - Security/Browser: route browser URL navigation through one SSRF-guarded validation path for tab-open/CDP-target/Playwright navigation flows and block private/metadata destinations by default (configurable via `browser.ssrfPolicy`). This ships in the next npm release. Thanks @dorjoos for reporting.
     - Security/Exec: for the next npm release, harden safe-bin stdin-only enforcement by blocking output/recursive flags (`sort -o/--output`, grep recursion) and tightening default safe bins to remove `sort`/`grep`, preventing safe-bin allowlist bypass for file writes/recursive reads. Thanks @nedlir for reporting.
    +- Security/Exec: block grep safe-bin positional operand bypass by setting grep positional budget to zero, so `-e/--regexp` cannot smuggle bare filename reads (for example `.env`) via ambiguous positionals; safe-bin grep patterns must come from `-e/--regexp`. This ships in the next npm release. Thanks @athuljayaram for reporting.
     - Security/Gateway/Agents: remove implicit admin scopes from agent tool gateway calls by classifying methods to least-privilege operator scopes, and enforce owner-only tooling (`cron`, `gateway`, `whatsapp_login`) through centralized tool-policy wrappers plus tool metadata to prevent non-owner DM privilege escalation. Ships in the next npm release. Thanks @Adam55A-code for reporting.
     - Security/Gateway: centralize gateway method-scope authorization and default non-CLI gateway callers to least-privilege method scopes, with explicit CLI scope handling, full core-handler scope classification coverage, and regression guards to prevent scope drift.
     - Security/Net: block SSRF bypass via NAT64 (`64:ff9b::/96`, `64:ff9b:1::/48`), 6to4 (`2002::/16`), and Teredo (`2001:0000::/32`) IPv6 transition addresses, and fail closed on IPv6 parse errors. Thanks @jackhax.
    
  • docs/tools/exec-approvals.md+2 0 modified
    @@ -146,6 +146,8 @@ Default safe bins: `jq`, `cut`, `uniq`, `head`, `tail`, `tr`, `wc`.
     
     `grep` and `sort` are not in the default list. If you opt in, keep explicit allowlist entries for
     their non-stdin workflows.
    +For `grep` in safe-bin mode, provide the pattern with `-e`/`--regexp`; positional pattern form is
    +rejected so file operands cannot be smuggled as ambiguous positionals.
     
     ## Control UI editing
     
    
  • src/infra/exec-approvals.test.ts+16 0 modified
    @@ -497,6 +497,22 @@ describe("exec approvals safe bins", () => {
           safeBins: ["grep"],
           executableName: "grep",
         },
    +    {
    +      name: "blocks grep file positional when pattern uses -e",
    +      argv: ["grep", "-e", "needle", ".env"],
    +      resolvedPath: "/usr/bin/grep",
    +      expected: false,
    +      safeBins: ["grep"],
    +      executableName: "grep",
    +    },
    +    {
    +      name: "blocks grep file positional after -- terminator",
    +      argv: ["grep", "-e", "needle", "--", ".env"],
    +      resolvedPath: "/usr/bin/grep",
    +      expected: false,
    +      safeBins: ["grep"],
    +      executableName: "grep",
    +    },
       ];
     
       for (const testCase of cases) {
    
  • src/infra/exec-safe-bin-policy.test.ts+22 0 added
    @@ -0,0 +1,22 @@
    +import { describe, expect, it } from "vitest";
    +import { SAFE_BIN_PROFILES, validateSafeBinArgv } from "./exec-safe-bin-policy.js";
    +
    +describe("exec safe bin policy grep", () => {
    +  const grepProfile = SAFE_BIN_PROFILES.grep;
    +
    +  it("allows stdin-only grep when pattern comes from flags", () => {
    +    expect(validateSafeBinArgv(["-e", "needle"], grepProfile)).toBe(true);
    +    expect(validateSafeBinArgv(["--regexp=needle"], grepProfile)).toBe(true);
    +  });
    +
    +  it("blocks grep positional pattern form to avoid filename ambiguity", () => {
    +    expect(validateSafeBinArgv(["needle"], grepProfile)).toBe(false);
    +  });
    +
    +  it("blocks file positionals when pattern comes from -e/--regexp", () => {
    +    expect(validateSafeBinArgv(["-e", "SECRET", ".env"], grepProfile)).toBe(false);
    +    expect(validateSafeBinArgv(["--regexp", "KEY", "config.py"], grepProfile)).toBe(false);
    +    expect(validateSafeBinArgv(["--regexp=KEY", ".env"], grepProfile)).toBe(false);
    +    expect(validateSafeBinArgv(["-e", "KEY", "--", ".env"], grepProfile)).toBe(false);
    +  });
    +});
    
  • src/infra/exec-safe-bin-policy.ts+4 1 modified
    @@ -91,7 +91,10 @@ export const SAFE_BIN_PROFILE_FIXTURES: Record<string, SafeBinProfileFixture> =
         ],
       },
       grep: {
    -    maxPositional: 1,
    +    // Keep grep stdin-only: pattern must come from -e/--regexp.
    +    // Allowing one positional is ambiguous because -e consumes the pattern and
    +    // frees the positional slot for a filename.
    +    maxPositional: 0,
         valueFlags: [
           "--regexp",
           "--file",
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

4

News mentions

0

No linked articles in our index yet.