{"id":"759e5355523c6914","repo":"pypa/pip","slug":"paths-must-be-inside-source-tree","errorCode":null,"errorMessage":"paths must be inside source tree","messagePattern":"paths must be inside source tree","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/pyproject_hooks/_impl.py","lineNumber":121,"sourceCode":"    \"\"\"Normalise and check a backend path.\n\n    Ensure that the requested backend path is specified as a relative path,\n    and resolves to a location under the given source tree.\n\n    Return an absolute version of the requested path.\n    \"\"\"\n    if os.path.isabs(requested):\n        raise ValueError(\"paths must be relative\")\n\n    abs_source = os.path.abspath(source_tree)\n    abs_requested = os.path.normpath(os.path.join(abs_source, requested))\n    # We have to use commonprefix for Python 2.7 compatibility. So we\n    # normalise case to avoid problems because commonprefix is a character\n    # based comparison :-(\n    norm_source = os.path.normcase(abs_source)\n    norm_requested = os.path.normcase(abs_requested)\n    if os.path.commonprefix([norm_source, norm_requested]) != norm_source:\n        raise ValueError(\"paths must be inside source tree\")\n\n    return abs_requested\n\n\nclass BuildBackendHookCaller:\n    \"\"\"A wrapper to call the build backend hooks for a source directory.\"\"\"\n\n    def __init__(\n        self,\n        source_dir: str,\n        build_backend: str,\n        backend_path: Optional[Sequence[str]] = None,\n        runner: Optional[\"SubprocessRunner\"] = None,\n        python_executable: Optional[str] = None,\n    ) -> None:\n        \"\"\"\n        :param source_dir: The source directory to invoke the build backend for\n        :param build_backend: The build backend spec","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/pyproject_hooks/_impl.py#L103-L139","documentation":"pyproject_hooks' norm_and_check raises ValueError('paths must be inside source tree') when a relative backend-path entry, after normalization, escapes the source tree. This guards against directory traversal: an entry like '../sibling' would resolve outside the project and is rejected.","triggerScenarios":"Constructing BuildBackendHookCaller with backend_path containing a traversal such as '../backend' or subpaths that, once joined and normcased, no longer share the source tree prefix (os.path.commonprefix check fails).","commonSituations":"Backend code living outside the project directory referenced via '../'; symlinks or case-insensitive path mismatches on macOS/Windows; refactoring that moved the backend without updating pyproject.toml.","solutions":["Move the backend code inside the source tree and use a path with no '..' components.","Remove the offending traversal entry from backend-path in pyproject.toml.","Ensure source_dir passed to BuildBackendHookCaller is the project root the backend lives under."],"exampleFix":"# before\n[build-system]\nbackend-path = [\"../shared_backend\"]\n\n# after\n[build-system]\nbackend-path = [\"shared_backend\"]  # after moving it inside the project","handlingStrategy":"validation","validationCode":"import os\nabs_src = os.path.abspath(source_tree)\nfor p in backend_paths:\n    abs_p = os.path.normpath(os.path.join(abs_src, p))\n    if os.path.commonprefix([os.path.normcase(abs_src), os.path.normcase(abs_p)]) != os.path.normcase(abs_src):\n        raise ValueError(f'backend-path escapes source tree: {p!r}')","typeGuard":"def is_inside_tree(source_tree: str, p: str) -> bool:\n    import os\n    a = os.path.normcase(os.path.normpath(os.path.join(os.path.abspath(source_tree), p)))\n    s = os.path.normcase(os.path.abspath(source_tree))\n    return a == s or a.startswith(s + os.sep)","tryCatchPattern":"try:\n    caller = BuildBackendHookCaller(src, backend, backend_path=paths)\nexcept ValueError:\n    paths = [p for p in paths if not p.startswith('..')]\n    caller = BuildBackendHookCaller(src, backend, backend_path=paths)","preventionTips":["Avoid '..' components in backend-path.","Keep backend code under the project root."],"tags":["pyproject-hooks","pep517","config","security","build"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}