shareAI-lab/learn-claude-code · error · ValueError

Path escapes workspace: {p}

Error message

Path escapes workspace: {p}

What it means

The s02 session's copy of safe_path(): it joins a relative path onto WORKDIR, resolves it, and raises this ValueError when the result falls outside WORKDIR. It is the shared gate for run_read and the other file tools introduced in that session. Because .resolve() follows symlinks, both explicit ../ traversal and symlink-based escapes are caught.

Source

Thrown at s02_tool_use/code.py:74

        return "Error: Dangerous command blocked"
    try:
        r = subprocess.run(command, shell=True, cwd=WORKDIR,
                           capture_output=True, text=True,
                           encoding="utf-8", errors="replace", timeout=120)
        out = (r.stdout + r.stderr).strip()
        return out[:50000] if out else "(no output)"
    except subprocess.TimeoutExpired:
        return "Error: Timeout (120s)"
    except (FileNotFoundError, OSError) as e:
        return f"Error: {e}"


# -- New in s02: four tools --

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path


def run_read(path: str, limit: int | None = None) -> str:
    try:
        lines = safe_path(path).read_text().splitlines()
        if limit and limit < len(lines):
            lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
        return "\n".join(lines)
    except Exception as e:
        return f"Error: {e}"


def run_write(path: str, content: str) -> str:
    try:
        file_path = safe_path(path)
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text(content)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Express every path relative to WORKDIR with no .. components
  2. Convert legitimate absolute paths via Path(p).resolve().relative_to(WORKDIR.resolve())
  3. Remove or relocate symlinks inside WORKDIR that point outside it
  4. Confirm WORKDIR matches the project root the agent believes it is in

Example fix

// before
run_read("../shared/config.yaml")
// after
run_read("shared/config.yaml")  # assuming shared/ lives under WORKDIR
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def safe_rel(p: str) -> str:
    cand = Path(p)
    if not cand.is_absolute():
        cand = WORKDIR / cand
    cand = cand.resolve()
    assert cand.is_relative_to(WORKDIR.resolve()), f"{p} escapes workspace"
    return str(cand.relative_to(WORKDIR.resolve()))

run_read(safe_rel(user_path))

Prevention

When it happens

Trigger: run_read("../../etc/passwd"); run_read("/etc/hosts") since joining an absolute path discards WORKDIR; a path stepping through a symlink inside WORKDIR that points to /usr or another outside location.

Common situations: Agents passing absolute paths echoed by earlier tool output; workspaces with vendored symlinked dependencies resolving outside the tree; WORKDIR and actual file location disagreeing after a project move.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/e3e8ac7e3267e493. Report an issue: GitHub.