{"id":"ab7b442c0f9d607a","repo":"pypa/pip","slug":"the-zip-file-has-a-file-trying-to-instal","errorCode":null,"errorMessage":"The zip file ({}) has a file ({}) trying to install outside target directory ({})","messagePattern":"The zip file \\((.+?)\\) has a file \\((.+?)\\) trying to install outside target directory \\((.+?)\\)","errorType":"exception","errorClass":"InstallationError","httpStatus":null,"severity":"critical","filePath":"src/pip/_internal/utils/unpacking.py","lineNumber":144,"sourceCode":"    \"\"\"\n    ensure_dir(location)\n    zipfp = open(filename, \"rb\")\n    try:\n        zip = zipfile.ZipFile(zipfp, allowZip64=True)\n        leading = has_leading_dir(zip.namelist()) and flatten\n        for info in zip.infolist():\n            name = info.filename\n            fn = name\n            if leading:\n                fn = split_leading_dir(name)[1]\n            fn = os.path.join(location, fn)\n            dir = os.path.dirname(fn)\n            if not is_within_directory(location, fn):\n                message = (\n                    \"The zip file ({}) has a file ({}) trying to install \"\n                    \"outside target directory ({})\"\n                )\n                raise InstallationError(message.format(filename, fn, location))\n            if fn.endswith((\"/\", \"\\\\\")):\n                # A directory\n                ensure_dir(fn)\n            else:\n                ensure_dir(dir)\n                # Don't use read() to avoid allocating an arbitrarily large\n                # chunk of memory for the file's content\n                fp = zip.open(name)\n                try:\n                    with open(fn, \"wb\") as destfp:\n                        shutil.copyfileobj(fp, destfp)\n                finally:\n                    fp.close()\n                    if zip_item_is_executable(info):\n                        set_extracted_file_to_default_mode_plus_executable(fn)\n    finally:\n        zipfp.close()\n","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/utils/unpacking.py#L126-L162","documentation":"Security InstallationError raised while unzipping an archive when a member resolves to a path outside the destination directory (a Zip Slip / path-traversal attempt). For each zip entry pip computes the joined target path and rejects it via is_within_directory before any file is written. This blocks archives containing entries like `../../etc/passwd`.","triggerScenarios":"Calling unzip_file (or unpacking a .zip/.whl/.egg via unpack_file) where an entry's name, after join with the destination, escapes the destination. Triggered by a malicious or malformed archive containing `../` sequences or absolute paths.","commonSituations":"Installing a malicious wheel/sdist from an untrusted index; a corrupted release artifact; CI pulling an archive from an untrusted mirror.","solutions":["Treat this as a security signal: do not whitelist; inspect the archive contents with `unzip -l file`.","Re-download the artifact from the official index and verify its hash.","If you control the archive, regenerate it without path-traversal entries.","Pin your index URL and verify packages over HTTPS."],"exampleFix":"// before\nunzip_file('evil.whl', '/target')  # member '../../pwned' escapes\n\n// after\n# reject the artifact; rebuild from trusted source:\npython -m build && twine check dist/*","handlingStrategy":"validation","validationCode":"import zipfile, os\ndef safe_to_unpack(zip_path: str, dest: str) -> bool:\n    dest = os.path.realpath(dest)\n    with zipfile.ZipFile(zip_path) as z:\n        for name in z.namelist():\n            target = os.path.realpath(os.path.join(dest, name))\n            if not target.startswith(dest + os.sep) and target != dest:\n                return False\n    return True","typeGuard":"null","tryCatchPattern":"from pip._internal.exceptions import InstallationError\ntry:\n    unzip_file(archive, dest)\nexcept InstallationError as e:\n    if 'outside target directory' in str(e):\n        # security event: quarantine the archive\n        raise\n    raise","preventionTips":["Only install from trusted, HTTPS indexes with hash verification.","Scan sdists/wheels with `unzip -l` / `tar -tvf` in CI.","Pin index URLs and use --require-hashes.","Never extract user-supplied archives directly with pip on untrusted input."],"tags":["pip","zip","security","path-traversal","zip-slip"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}