{"record":{"id":"b928d1981b6cee88","repo":"BerriAI/litellm","slug":"invalid-file-path-file-path-r-path-traversal-de","errorCode":null,"errorMessage":"Invalid file path {file_path!r}: path traversal detected","messagePattern":"Invalid file path (.+?): path traversal detected","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"litellm/integrations/bitbucket/bitbucket_client.py","lineNumber":19,"sourceCode":"\"\"\"\nBitBucket API client for fetching .prompt files from BitBucket repositories.\n\"\"\"\n\nimport base64\nimport urllib.parse\nfrom typing import Any, Final\n\nfrom litellm.llms.custom_httpx.http_handler import HTTPHandler\n\n\ndef _sanitize_file_path(file_path: str) -> str:\n    \"\"\"Reject path traversal and URL-encode each path segment.\"\"\"\n    if \"#\" in file_path or \"?\" in file_path:\n        raise ValueError(f\"Invalid file path {file_path!r}: contains URL special characters\")\n    parts: Final = file_path.split(\"/\")\n    for part in parts:\n        if part == \"..\":\n            raise ValueError(f\"Invalid file path {file_path!r}: path traversal detected\")\n    return \"/\".join(urllib.parse.quote(part, safe=\"\") for part in parts)\n\n\nclass BitBucketClient:\n    \"\"\"\n    Client for interacting with BitBucket API to fetch .prompt files.\n\n    Supports:\n    - Authentication with access tokens\n    - Fetching file contents from repositories\n    - Team-based access control through BitBucket permissions\n    - Branch-specific file fetching\n    \"\"\"\n\n    def __init__(self, config: dict[str, Any]):\n        \"\"\"\n        Initialize the BitBucket client.\n","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/bitbucket/bitbucket_client.py#L1-L37","documentation":"The second branch of _sanitize_file_path: after splitting the path on '/', any segment equal to '..' triggers a path-traversal rejection. This prevents crafted paths from escaping the intended repository directory in the constructed BitBucket API URL. The check runs on the literal segment value before URL-encoding, so an encoded '%2E%2E' that later decodes to '..' upstream would still need to be literal here to be caught — the guard is on the raw input.","triggerScenarios":"Passing a file path with a '..' segment such as 'prompts/../../secrets/config' to get_file; prompt_id sourced from untrusted user input concatenated into a path; test payloads for path traversal.","commonSituations":"Prompt IDs built by string concatenation with user input; attempting to reference files outside the configured prompts directory; security scanners probing the integration.","solutions":["Use a flat or explicitly allow-listed path — drop '..' segments from the file path / prompt_id","Canonicalize and re-verify external paths (e.g. os.path.normpath then confirm the result stays under the allowed root) before passing them in","Rename repository layout if legitimate files need relative-style references"],"exampleFix":"# before\nclient.get_file(\"prompts/../internal/admin.prompt\")  # ValueError: path traversal detected\n\n# after\nclient.get_file(\"internal/admin.prompt\")  # reference the target directly","handlingStrategy":"validation","validationCode":"import posixpath\n\ndef normalize_repo_path(root: str, file_path: str) -> str:\n    if \"..\" in file_path.split(\"/\"):\n        raise ValueError(\"path traversal not allowed\")\n    normalized = posixpath.normpath(f\"{root}/{file_path}\").lstrip(\"/\")\n    if normalized.startswith(\"..\") or normalized.startswith(\"/\"):\n        raise ValueError(\"path escapes allowed root\")\n    return normalized","typeGuard":"def is_within_root(root: str, p: str) -> bool:\n    if not isinstance(p, str) or \"..\" in p.split(\"/\"):\n        return False\n    n = posixpath.normpath(p)\n    return not n.startswith(\"..\") and not posixpath.isabs(n)","tryCatchPattern":"try:\n    client.get_file(prompt_id)\nexcept ValueError as e:\n    if \"path traversal\" in str(e):\n        # untrusted input: log the security event and reject the request entirely\n        security_log.warning(\"rejected traversal path: %r\", prompt_id)\n        raise\n    raise","preventionTips":["Treat prompt IDs / paths from users as untrusted; reject '..' segments at the API boundary","Prefer flat IDs mapped through a server-side lookup table to actual paths","Run path normalization (normpath) plus a prefix check against the allowed root before any fetch","Log and alert on traversal attempts — they indicate probing"],"tags":["bitbucket","path-traversal","security","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}