{"record":{"id":"7d6a116fbdcda201","repo":"langchain-ai/deepagents","slug":"path-traversal-not-allowed-path","errorCode":null,"errorMessage":"Path traversal not allowed: {path}","messagePattern":"Path traversal not allowed: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/utils.py","lineNumber":702,"sourceCode":"            Windows absolute path (e.g., `C:/...`), or does not start with an\n            allowed prefix when `allowed_prefixes` is specified.\n\n    Example:\n        ```python\n        validate_path(\"foo/bar\")  # Returns: \"/foo/bar\"\n        validate_path(\"/./foo//bar\")  # Returns: \"/foo/bar\"\n        validate_path(\"../etc/passwd\")  # Raises ValueError\n        validate_path(r\"C:\\\\Users\\\\file.txt\")  # Raises ValueError\n        validate_path(\"/data/file.txt\", allowed_prefixes=[\"/data/\"])  # OK\n        validate_path(\"/etc/file.txt\", allowed_prefixes=[\"/data/\"])  # Raises ValueError\n        ```\n    \"\"\"\n    # Check for traversal as a path component (not substring) to avoid\n    # false-positive rejection of legitimate filenames like \"foo..bar.txt\"\n    parts = PurePosixPath(to_posix_path(path)).parts\n    if \"..\" in parts or path.startswith(\"~\"):\n        msg = f\"Path traversal not allowed: {path}\"\n        raise ValueError(msg)\n\n    # Reject Windows absolute paths (e.g., C:\\..., D:/...)\n    if re.match(r\"^[a-zA-Z]:\", path):\n        msg = f\"Windows absolute paths are not supported: {path}. Please use virtual paths starting with / (e.g., /workspace/file.txt)\"\n        raise ValueError(msg)\n\n    normalized = os.path.normpath(path)\n    normalized = normalized.replace(\"\\\\\", \"/\")\n\n    if not normalized.startswith(\"/\"):\n        normalized = f\"/{normalized}\"\n\n    # Defense-in-depth: verify normpath didn't produce traversal\n    if \"..\" in normalized.split(\"/\"):\n        msg = f\"Path traversal detected after normalization: {path} -> {normalized}\"\n        raise ValueError(msg)\n\n    if allowed_prefixes is not None and not any(normalized.startswith(prefix) for prefix in allowed_prefixes):","sourceCodeStart":684,"sourceCodeEnd":720,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/utils.py#L684-L720","documentation":"`validate_path` normalizes virtual filesystem paths and rejects anything that escapes the virtual root. A `..` path component or a leading `~` raises ValueError 'Path traversal not allowed'. The check is per path component (not substring), so legitimate names like `foo..bar.txt` still pass.","triggerScenarios":"Calling backend `read`/`write`/`ls`/`edit`/`glob` with paths like `/../etc/passwd`, `a/../../b`, or `~/notes.txt`; LLM-generated tool arguments containing `..` or `~`; user input interpolated unvalidated into paths.","commonSituations":"Agents hallucinating home-relative (`~`) paths; users pasting shell-style paths; composing paths from user-controlled directory names without sanitization.","solutions":["Use absolute virtual paths rooted at `/` (e.g. `/workspace/file.txt`) without `..` or `~` components","Resolve/normalize paths against an explicit base directory yourself and verify containment before calling the backend","Sanitize user/LLM input: strip `~`, collapse or reject `..` components","Catch the ValueError and return a friendly tool-error telling the agent to use virtual paths"],"exampleFix":"// before\nbackend.read('~/secrets.txt')\nbackend.read('/workspace/../etc/passwd')\n// after\nbackend.read('/workspace/notes.txt')","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\ndef is_safe_virtual_path(path: str) -> bool:\n    if path.startswith('~'):\n        return False\n    parts = PurePosixPath(path.replace('\\\\', '/')).parts\n    if '..' in parts:\n        return False\n    return True\n\nif is_safe_virtual_path(p):\n    backend.read(p)","typeGuard":"def is_safe_path(path: object) -> bool:\n    if not isinstance(path, str) or path.startswith('~'):\n        return False\n    from pathlib import PurePosixPath\n    return '..' not in PurePosixPath(path.replace('\\\\', '/')).parts","tryCatchPattern":"try:\n    content = backend.read(path)\nexcept ValueError as exc:\n    if 'Path traversal not allowed' in str(exc):\n        raise ToolError(f'use absolute /-rooted virtual paths, got {path!r}') from exc\n    raise","preventionTips":["Validate/normalize agent- and user-supplied paths before backend calls","Reject `~` and `..` components in tool-input schemas","Always use absolute virtual paths rooted at `/`","Document the virtual path convention in agent prompts so models emit valid paths"],"tags":["path-traversal","security","validation"],"backgroundTag":"path-traversal-rejected","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}