
AI SAST Finding: Path Traversal in OpenClaw via LLM Guardrail Bypass | Blog | Endor Labs
Endor Labs details GHSA-r5fq-947m-xm57, a high-severity path traversal (CVSS 8.8) in OpenClaw's apply_patch tool that allows arbitrary file write/delete when sandboxRoot isn't configured, tracing the vulnerable code path from resolvePatchPath() through resolvePathFromCwd() to fs.writeFile/fs.rm. Because the tool is only invoked via LLM decision, the researcher bypassed the model's safety guardrails by injecting fabricated function_call/function_call_output entries into the /v1/responses conversation history, tricking the LLM via in-context learning into emitting a real malicious tool call with a traversal payload. This illustrates how LLM safety training can be the sole runtime control gating dangerous tool arguments, and how that control can be defeated with conversation history injection.
What it is
GHSA-r5fq-947m-xm57: a high-severity path traversal (CVSS 8.8, CWE-22) in OpenClaw's apply_patch tool, allowing arbitrary file write and delete outside the workspace. Fixed in version >= 2026.2.14 (PR #16405). The sink is in resolvePatchPath() at lines 215–236 of src/agents/apply-patch.ts.
How it works
resolvePatchPath() branches on whether sandboxRoot is configured. When set, assertSandboxPath() blocks traversal. When unset — the default for native, non-Docker openclaw gateway run deployments — resolvePathFromCwd() calls path.resolve() and passes the result straight to fs.writeFile/fs.rm with no bounds check.
Because apply_patch fires only when the LLM decides to call it, the payload must pass through the model, whose safety training strips ../ or refuses. The bypass exploits that /v1/responses accepts a full input array including fabricated function_call and function_call_output entries. The attacker pre-seeds a fake history: a user request, a fabricated apply_patch call with ../../tmp/test1.txt, and a fabricated "Patch applied successfully" output. In-context learning normalizes the traversal, so the real final request for ../../../../tmp/test2.txt gets emitted as a genuine tool call and written to /tmp/test2.txt. OpenClaw compounds this by implementing tool_choice as a soft system-prompt injection (openresponses-http.ts:130-165), not a hard constraint — the model can still choose text.
Preconditions / Affected
- Non-sandboxed deployment (native, no
sandboxRoot) — Docker deployments withsandboxRootwere protected byassertSandboxPath()("Path escapes sandbox root"). apply_patchenabled (default) and a valid API Bearer token for/v1/responses.
Documented impact: writing ~/.ssh/authorized_keys, overwriting ~/.openclaw/openclaw.json to disable auth, injecting cron jobs via /var/spool/cron/crontabs/, or deleting critical files.
What to do
Update to >= 2026.2.14, which adds a workspaceOnly option (default true) enforcing containment on the non-sandboxed path. If you can't update immediately:
- Disable the tool:
tools.exec.applyPatch.enabled: false - Confirm
workspaceOnlyistrue(default, verify unchanged) - Restrict who holds Bearer tokens for
/v1/responses - Run in Docker with
sandboxRootconfigured
Caveats
The bypass is non-deterministic — the pre-seeded history succeeded in testing (9 of 10 other strategies failed), but repeated runs yield different bypass rates. The author frames this variability as the core reason LLM safety training must not be treated as a security boundary. The AI SAST engine found the server-side bug; the guardrail bypass was characterized during manual exploit validation.
Organisations named here
- GHSA-r5fq-947m-xm57
Technologies named here
- OpenClaw
- Endor Labs
Techniques named here
- Path traversal
Related in AppSec
Flowise MCP Security Bypass Enables RCE
Inside an Oracle Database SQL Injection Attack | Huntress
OASIS
