VYPR
High severity7.5NVD Advisory· Published Mar 26, 2026· Updated Mar 31, 2026

CVE-2026-32846

CVE-2026-32846

Description

OpenClaw through 2026.3.23 (fixed in commit 4797bbc) contains a path traversal vulnerability in media parsing that allows attackers to read arbitrary files by bypassing path validation in the isLikelyLocalPath() and isValidMedia() functions. Attackers can exploit incomplete validation and the allowBareFilename bypass to reference files outside the intended application sandbox, resulting in disclosure of sensitive information including system files, environment files, and SSH keys.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
< 2026.03.282026.03.28

Affected products

1
  • cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*
    Range: <=2026.3.23

Patches

1
4797bbc5b96e

fix: reject path traversal and home-dir patterns in media parse layer (#54642)

https://github.com/openclaw/openclawDevin RobisonMar 25, 2026via ghsa
2 files changed · +52 7
  • src/media/parse.test.ts+15 2 modified
    @@ -12,8 +12,6 @@ describe("splitMediaFromOutput", () => {
         const pathCases = [
           ["/Users/pete/My File.png", "MEDIA:/Users/pete/My File.png"],
           ["/Users/pete/My File.png", 'MEDIA:"/Users/pete/My File.png"'],
    -      ["~/Pictures/My File.png", "MEDIA:~/Pictures/My File.png"],
    -      ["../../etc/passwd", "MEDIA:../../etc/passwd"],
           ["./screenshots/image.png", "MEDIA:./screenshots/image.png"],
           ["media/inbound/image.png", "MEDIA:media/inbound/image.png"],
           ["./screenshot.png", "  MEDIA:./screenshot.png"],
    @@ -31,6 +29,21 @@ describe("splitMediaFromOutput", () => {
         }
       });
     
    +  it("rejects traversal and home-dir paths and strips them from output", () => {
    +    const traversalCases = [
    +      "MEDIA:../../../etc/passwd",
    +      "MEDIA:../../.env",
    +      "MEDIA:~/.ssh/id_rsa",
    +      "MEDIA:~/Pictures/My File.png",
    +      "MEDIA:./foo/../../../etc/shadow",
    +    ];
    +    for (const input of traversalCases) {
    +      const result = splitMediaFromOutput(input);
    +      expect(result.mediaUrls, `should reject media: ${input}`).toBeUndefined();
    +      expect(result.text, `should strip from text: ${input}`).toBe("");
    +    }
    +  });
    +
       it("keeps audio_as_voice detection stable across calls", () => {
         const input = "Hello [[audio_as_voice]]";
         const first = splitMediaFromOutput(input);
    
  • src/media/parse.ts+37 5 modified
    @@ -18,10 +18,21 @@ const WINDOWS_DRIVE_RE = /^[a-zA-Z]:[\\/]/;
     const SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
     const HAS_FILE_EXT = /\.\w{1,10}$/;
     
    -// Recognize local file path patterns. Security validation is deferred to the
    -// load layer (loadWebMedia / resolveSandboxedMediaSource) which has the context
    -// needed to enforce sandbox roots and allowed directories.
    -function isLikelyLocalPath(candidate: string): boolean {
    +// Matches ".." as a standalone path segment (start, middle, or end).
    +const TRAVERSAL_SEGMENT_RE = /(?:^|[/\\])\.\.(?:[/\\]|$)/;
    +
    +function hasTraversalOrHomeDirPrefix(candidate: string): boolean {
    +  return (
    +    candidate.startsWith("../") ||
    +    candidate === ".." ||
    +    candidate.startsWith("~") ||
    +    TRAVERSAL_SEGMENT_RE.test(candidate)
    +  );
    +}
    +
    +// Broad structural check: does this look like a local file path? Used only for
    +// stripping MEDIA: lines from output text — never for media approval.
    +function looksLikeLocalFilePath(candidate: string): boolean {
       return (
         candidate.startsWith("/") ||
         candidate.startsWith("./") ||
    @@ -33,6 +44,21 @@ function isLikelyLocalPath(candidate: string): boolean {
       );
     }
     
    +// Recognize safe local file path patterns for media approval, rejecting
    +// traversal and home-dir paths so they never reach downstream load/send logic.
    +function isLikelyLocalPath(candidate: string): boolean {
    +  if (hasTraversalOrHomeDirPrefix(candidate)) {
    +    return false;
    +  }
    +  return (
    +    candidate.startsWith("/") ||
    +    candidate.startsWith("./") ||
    +    WINDOWS_DRIVE_RE.test(candidate) ||
    +    candidate.startsWith("\\\\") ||
    +    (!SCHEME_RE.test(candidate) && (candidate.includes("/") || candidate.includes("\\")))
    +  );
    +}
    +
     function isValidMedia(
       candidate: string,
       opts?: { allowSpaces?: boolean; allowBareFilename?: boolean },
    @@ -54,6 +80,12 @@ function isValidMedia(
         return true;
       }
     
    +  // Hard reject traversal/home-dir patterns before the bare-filename fallback
    +  // to prevent path traversal bypasses (e.g. "../../.env" matching HAS_FILE_EXT).
    +  if (hasTraversalOrHomeDirPrefix(candidate)) {
    +    return false;
    +  }
    +
       // Accept bare filenames (e.g. "image.png") only when the caller opts in.
       // This avoids treating space-split path fragments as separate media items.
       if (opts?.allowBareFilename && !SCHEME_RE.test(candidate) && HAS_FILE_EXT.test(candidate)) {
    @@ -169,7 +201,7 @@ export function splitMediaFromOutput(raw: string): {
     
           const trimmedPayload = payloadValue.trim();
           const looksLikeLocalPath =
    -        isLikelyLocalPath(trimmedPayload) || trimmedPayload.startsWith("file://");
    +        looksLikeLocalFilePath(trimmedPayload) || trimmedPayload.startsWith("file://");
           if (
             !unwrapped &&
             validCount === 1 &&
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.