{"record":{"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/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/utils/unpacking.py#L126-L162","documentation":"Raised as InstallationError by unzip_file (unpacking.py:144) when a member of a zip archive would be extracted outside the target directory. This is the classic Zip-Slip / path-traversal protection: a malicious zip with entries like ../../etc/cron.d/evil could overwrite system files. The is_within_directory check at line 139 verifies every member's resolved path stays inside `location`; any violation raises before the file is written.","triggerScenarios":"During installation of a sdist or archive, unzip_file iterates zip members, computes the extraction path fn at line 137, and checks is_within_directory(location, fn) at line 139. A member with ../ sequences or absolute paths that escape `location` triggers the raise.","commonSituations":"A malicious or buggy sdist package whose archive contains path-traversal entries. A corrupted download that altered zip member names. A package built with a broken build tool that generated invalid relative paths in the archive.","solutions":["Verify the package source is trusted and the archive is not corrupted (re-download from the official index).","Report the package to the index/maintainer if it contains suspicious traversal entries — it may be compromised.","Avoid installing from untrusted direct URLs; prefer packages from vetted indices.","If you control the archive, rebuild it without ../ or absolute paths in member names."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import zipfile, os\n\ndef check_zip_for_traversal(zip_path: str, dest: str) -> list[str]:\n    \"\"\"Return list of zip members that would escape the destination directory.\"\"\"\n    dest = os.path.abspath(dest)\n    dangerous = []\n    with zipfile.ZipFile(zip_path) as zf:\n        for name in zf.namelist():\n            target = os.path.abspath(os.path.join(dest, name))\n            if not target.startswith(dest + os.sep) and target != dest:\n                dangerous.append(name)\n    return dangerous","typeGuard":"import os\n\ndef is_within_directory(directory: str, target: str) -> bool:\n    \"\"\"True if target path stays within directory (no traversal).\"\"\"\n    abs_dir = os.path.realpath(directory)\n    abs_target = os.path.realpath(target)\n    return abs_target == abs_dir or abs_target.startswith(abs_dir + os.sep)","tryCatchPattern":"from pip._internal.exceptions import InstallationError\n\ntry:\n    # unzip / install operation\n    pass\nexcept InstallationError as e:\n    if 'trying to install outside target directory' in str(e):\n        # Zip-Slip detected: do NOT extract; report as security issue\n        pass","preventionTips":["Only install packages from trusted indices (PyPI) that scan for malicious archives.","Pre-validate downloaded archives for path traversal before extraction.","Prefer wheels over sdists when available — wheels are less prone to traversal issues.","Treat Zip-Slip detection as a security incident, not a routine error."],"tags":["security","zip-slip","path-traversal","unpacking","zipfile"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}