{"record":{"id":"9a1c2937d21d913e","repo":"shareAI-lab/learn-claude-code","slug":"worktree-path-escapes-directory-name-r-9a1c29","errorCode":null,"errorMessage":"Worktree path escapes directory: {name!r}","messagePattern":"Worktree path escapes directory: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s15_integrated_harness/code.py","lineNumber":391,"sourceCode":"WORKTREES_ROOT = WORKTREES_DIR.resolve()\nVALID_WORKTREE_NAME = re.compile(r\"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$\")\n\n\ndef validate_worktree_name(name: str) -> str | None:\n    if not isinstance(name, str) or not VALID_WORKTREE_NAME.fullmatch(name):\n        return (\"worktree name must be 1-64 letters, digits, dots, \"\n                \"underscores, or dashes, and start with a letter or digit\")\n    if name in {\".\", \"..\"} or \"..\" in name:\n        return \"worktree name cannot contain '..'\"\n    return None\n\n\ndef _worktree_path(name: str) -> Path:\n    path = (WORKTREES_DIR / name).resolve()\n    if (not WORKTREES_ROOT.is_relative_to(WORKDIR.resolve())\n            or not path.is_relative_to(WORKTREES_ROOT)\n            or path == WORKTREES_ROOT):\n        raise ValueError(f\"Worktree path escapes directory: {name!r}\")\n    return path\n\n\ndef _worktree_branch(name: str) -> str:\n    return f\"wt/{name}\"\n\n\ndef _run_git(args: list[str], cwd: Path | None = None) -> tuple[bool, str]:\n    \"\"\"Run Git without shell interpolation and return (ok, combined output).\"\"\"\n    try:\n        result = subprocess.run(\n            [\"git\", *args], cwd=cwd or WORKDIR,\n            capture_output=True, text=True, timeout=30,\n        )\n    except (OSError, subprocess.TimeoutExpired) as exc:\n        return False, f\"{type(exc).__name__}: {exc}\"\n    output = (result.stdout + result.stderr).strip()\n    return result.returncode == 0, output or \"(no output)\"","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s15_integrated_harness/code.py#L373-L409","documentation":"_worktree_path() maps a worktree name to WORKTREES_DIR/<name> and refuses the result unless the resolved path stays strictly inside WORKTREES_ROOT and is not the root itself. This blocks traversal via names that survive _validate_worktree_name (e.g. names containing separators after odd normalization) and symlinked worktrees pointing elsewhere.","triggerScenarios":"Passing a worktree name that, once joined and resolved, lands outside the worktrees root — e.g. an absolute path (the join semantics can flip), a name whose target inside WORKTREES_DIR is a symlink to another location, or a name resolving exactly to WORKTREES_ROOT itself.","commonSituations":"Model tool calls passing a path instead of a bare name; pre-created symlinks inside the worktrees directory; moving WORKTREES_DIR outside WORKDIR in config.","solutions":["Pass only a bare name matching the validator (1-64 chars of letters/digits/dots/underscores/dashes, no '..', leading alnum).","Remove symlinks inside the worktrees directory; worktrees must be real git worktrees created by the harness.","Keep WORKTREES_DIR physically under WORKDIR."],"exampleFix":"// before\nworktree_cwd_for(\"../shared-build\")\n\n// after\nworktree_cwd_for(\"shared-build\")  // bare name; harness resolves it under WORKTREES_DIR","handlingStrategy":"validation","validationCode":"import re\n\nWORKTREE_NAME_RE = re.compile(r\"[A-Za-z0-9][A-Za-z0-9._-]{0,63}\")\n\ndef is_valid_worktree_name(name: str) -> bool:\n    return (\n        isinstance(name, str)\n        and bool(WORKTREE_NAME_RE.fullmatch(name))\n        and name not in {\".\", \"..\"}\n        and \"..\" not in name\n    )","typeGuard":"def is_bare_worktree_name(name) -> bool:\n    return isinstance(name, str) and \"/\" not in name and \"\\\\\" not in name and name not in {\".\", \"..\"} and \"..\" not in name","tryCatchPattern":"try:\n    path = _worktree_path(name)\nexcept ValueError as e:\n    if \"escapes directory\" in str(e):\n        return f\"Error: invalid worktree name {name!r}\"\n    raise","preventionTips":["Pass bare names, never paths.","Reject names containing separators or '..' caller-side.","Keep WORKTREES_DIR free of symlinks."],"tags":["git","worktree","path-traversal","security"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}