{"record":{"id":"a601d9b5a0581f5d","repo":"harry0703/MoneyPrinterTurbo","slug":"path-is-outside-the-allowed-directory","errorCode":null,"errorMessage":"path is outside the allowed directory","messagePattern":"path is outside the allowed directory","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"app/utils/file_security.py","lineNumber":27,"sourceCode":") -> str:\n    # 用户传入的路径可能是文件名、相对路径、绝对路径，也可能夹带 `../`。\n    # 这里统一解析成真实路径，并用 commonpath 判断它是否仍在允许目录内。\n    # 这样比简单判断字符串前缀可靠，可以覆盖符号链接、重复分隔符、相对路径\n    # 等场景，适用于上传目录、素材目录、任务产物目录这类白名单目录。\n    if not unsafe_path:\n        raise ValueError(\"empty path is not allowed\")\n\n    base_dir_real = os.path.realpath(base_dir)\n    candidate_path = unsafe_path\n    if not os.path.isabs(candidate_path):\n        candidate_path = os.path.join(base_dir_real, candidate_path)\n\n    resolved_path = os.path.realpath(candidate_path)\n    try:\n        common_path = os.path.commonpath([base_dir_real, resolved_path])\n    except ValueError as exc:\n        # Windows 下不同盘符会触发 ValueError，这类路径一定不属于允许目录。\n        raise ValueError(\"path is outside the allowed directory\") from exc\n\n    if common_path != base_dir_real:\n        raise ValueError(\"path is outside the allowed directory\")\n\n    if require_file and not os.path.isfile(resolved_path):\n        raise ValueError(\"file does not exist\")\n\n    return resolved_path\n","sourceCodeStart":9,"sourceCodeEnd":36,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/utils/file_security.py#L9-L36","documentation":"Raised when os.path.commonpath raises ValueError while comparing the base directory against the resolved candidate. On Windows this happens when the two paths are on different drives (e.g. C:\\data vs D:\\attack), which by definition cannot be inside the allowed directory. The original exception is chained.","triggerScenarios":"On Windows, passing an absolute path on a different drive letter than base_dir (e.g. base 'C:\\app\\storage', path 'D:\\secret.txt'); passing a UNC path (\\\\\\\\server\\\\share) against a mapped drive.","commonSituations":"Windows deployments where the storage directory is on C: and a client submits a D: path; drive-relative paths like 'D:file.txt' resolving to the D: working directory.","solutions":["Submit paths that live under the allowed base directory; for absolute paths use the same drive.","Use relative paths under the base directory — they are joined to base_dir before resolution and never hit this branch."],"exampleFix":"# before (Windows)\nresolve_path_within_directory(r\"C:\\app\\storage\", r\"D:\\videos\\clip.mp4\")\n\n# after\nresolve_path_within_directory(r\"C:\\app\\storage\", \"clip.mp4\")  # relative to base","handlingStrategy":"validation","validationCode":"import os\nif os.name == \"nt\" and os.path.isabs(unsafe_path):\n    base_drive = os.path.splitdrive(os.path.realpath(base_dir))[0]\n    if os.path.splitdrive(unsafe_path)[0].upper() != base_drive.upper():\n        raise ValueError(\"path must be on the same drive as the storage directory\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["On Windows, always send relative paths or same-drive absolute paths.","Prefer filenames returned by the server over client-built absolute paths."],"tags":["path-traversal","security","windows"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}