{"record":{"id":"8188c993b57c7953","repo":"BerriAI/litellm","slug":"filename-contains-null-byte","errorCode":null,"errorMessage":"Filename contains null byte","messagePattern":"Filename contains null byte","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/common_utils/path_utils.py","lineNumber":59,"sourceCode":"def safe_filename(filename: str) -> str:\n    \"\"\"\n    Extract a safe filename from a user-supplied path.\n\n    Strips all directory components (both Unix and Windows separators),\n    returning only the final name. Use this for uploaded file names\n    before writing to disk.\n\n    Args:\n        filename: User-supplied filename (may contain path separators).\n\n    Returns:\n        The basename only, with no directory components.\n\n    Raises:\n        ValueError: If the resulting filename is empty or contains null bytes.\n    \"\"\"\n    if \"\\x00\" in filename:\n        raise ValueError(\"Filename contains null byte\")\n    # Normalize backslash separators for cross-platform safety\n    name: Final = filename.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n    if not name or name in (\".\", \"..\"):\n        raise ValueError(\"Empty or unsafe filename\")\n    return name\n","sourceCodeStart":41,"sourceCodeEnd":65,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/common_utils/path_utils.py#L41-L65","documentation":"safe_filename() takes a user-supplied filename (typically from an upload form) before writing it to disk. It first rejects any filename containing a NUL byte with this ValueError. The guard exists because NUL bytes cannot appear in real filenames and can confuse lower-level file APIs. The prompt-file conversion endpoint (prompt_endpoints.py) runs uploaded .prompt filenames through this function.","triggerScenarios":"POST a file upload (for example to the /prompts dotprompt conversion route) with a filename field containing \\x00, such as 'my.prompt\\x00.txt'. The filename comes from the multipart part header, so it is fully client-controlled.","commonSituations":"A hostile or fuzzed upload carries control characters in the filename. A client copies a filename from untrusted metadata without sanitizing it. A proxy bug injects stray bytes into part headers.","solutions":["Send a clean filename: basename only, extension '.prompt', no control characters.","Sanitize upstream: reject or strip \\x00 from any filename before forwarding the upload.","Call a basename + control-character filter in client code when filenames come from end users."],"exampleFix":"# before\nfiles = {\"file\": (\"idea.prompt\\x00.txt\", content)}\nrequests.post(url, files=files)\n\n# after\nfiles = {\"file\": (\"idea.prompt\", content)}\nrequests.post(url, files=files)","handlingStrategy":"validation","validationCode":"def safe_upload_name(filename: str) -> str | None:\n    if not filename or \"\\x00\" in filename:\n        return None\n    name = filename.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n    return name if name and name not in (\".\", \"..\") else None\n\nclean = safe_upload_name(user_filename)\nassert clean and clean.endswith(\".prompt\"), \"bad upload filename\"","typeGuard":"def is_safe_filename(filename: str) -> bool:\n    if not filename or \"\\x00\" in filename:\n        return False\n    name = filename.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n    return bool(name) and name not in (\".\", \"..\")","tryCatchPattern":"try:\n    resp = client.post(url, files={\"file\": (user_filename, content)})\n    resp.raise_for_status()\nexcept HTTPError as e:\n    if e.response.status_code in (400, 500) and \"null byte\" in e.response.text.lower():\n        resp = client.post(url, files={\"file\": (safe_upload_name(user_filename), content)})\n    raise","preventionTips":["Generate upload filenames server-side (slug or UUID plus extension) instead of passing user input.","Strip control characters from filenames at the boundary where user input enters.","Add a client-side filename lint step in test suites that upload files."],"tags":["litellm","security","file-upload","null-byte","path-validation","prompts"],"backgroundTag":"null-byte-in-path","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}