{"record":{"id":"b92d637c23de0135","repo":"pypa/pip","slug":"the-wheel-r-has-a-file-r-trying-to-install-o","errorCode":null,"errorMessage":"The wheel {!r} has a file {!r} trying to install outside the target directory {!r}","messagePattern":"The wheel (.+?) has a file (.+?) trying to install outside the target directory (.+?)","errorType":"exception","errorClass":"InstallationError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/operations/install/wheel.py","lineNumber":501,"sourceCode":"    def record_installed(\n        srcfile: RecordPath, destfile: str, modified: bool = False\n    ) -> None:\n        \"\"\"Map archive RECORD paths to installation RECORD paths.\"\"\"\n        newpath = _fs_to_record_path(destfile, lib_dir)\n        installed[srcfile] = newpath\n        if modified:\n            changed.add(newpath)\n\n    def is_dir_path(path: RecordPath) -> bool:\n        return path.endswith(\"/\")\n\n    def assert_no_path_traversal(dest_dir_path: str, target_path: str) -> None:\n        if not is_within_directory(dest_dir_path, target_path):\n            message = (\n                \"The wheel {!r} has a file {!r} trying to install\"\n                \" outside the target directory {!r}\"\n            )\n            raise InstallationError(\n                message.format(wheel_path, target_path, dest_dir_path)\n            )\n\n    def root_scheme_file_maker(\n        zip_file: ZipFile, dest: str\n    ) -> Callable[[RecordPath], File]:\n        def make_root_scheme_file(record_path: RecordPath) -> File:\n            normed_path = os.path.normpath(record_path)\n            dest_path = os.path.join(dest, normed_path)\n            assert_no_path_traversal(dest, dest_path)\n            return ZipBackedFile(record_path, dest_path, zip_file)\n\n        return make_root_scheme_file\n\n    def data_scheme_file_maker(\n        zip_file: ZipFile, scheme: Scheme\n    ) -> Callable[[RecordPath], File]:\n        scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/operations/install/wheel.py#L483-L519","documentation":"Raised as InstallationError by assert_no_path_traversal when a file extracted from a wheel resolves, after os.path.normpath/join, to a destination outside its target install directory. This blocks zip-slip / path-traversal attacks where archive entries use '..' or absolute paths to write outside the scheme directory.","triggerScenarios":"The wheel archive contains a RECORD path whose normalized destination escapes the lib_dir or scheme path; is_within_directory(dest_dir_path, target_path) is false for root-scheme or data-scheme files.","commonSituations":"Malicious or corrupted wheel; buggy wheel-building tool that emitted absolute or parent-referencing paths; tampered artefact from an untrusted index.","solutions":["Do not install the offending wheel; treat it as untrusted.","Rebuild the wheel with a standard build backend (setuptools/hatchling/flit/wheel) that emits sane paths.","Report the issue to the wheel author/maintainer.","Verify the wheel's hash/signature against the publisher."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import os\n\ndef is_within_directory(directory: str, target: str) -> bool:\n    abs_dir = os.path.realpath(directory)\n    abs_tgt = os.path.realpath(target)\n    return os.path.commonpath([abs_dir]) == os.path.commonpath([abs_dir, abs_tgt])\n\ndef wheel_paths_safe(record_paths, target: str) -> bool:\n    return all(is_within_directory(target, os.path.join(target, p)) for p in record_paths)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify wheel hashes/signatures against the publisher before installing.","Build wheels with standard backends that emit clean relative paths.","Scan untrusted wheels for '..' or absolute paths before install."],"tags":["security","wheel","path-traversal","zip-slip"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}