pypa/pip · critical · InstallationError

The tar file ({}) has a file ({}) trying to install outside

Error message

The tar file ({}) has a file ({}) trying to install outside target directory ({})

What it means

Path-traversal InstallationError from pip's legacy tar fallback (_untar_without_filter) used on Python versions without tarfile.data_filter. Raised when a member's resolved path is not inside the destination directory, either via textual `..` segments or via a symlink that later redirects a member outside. This is the Zip-Slip equivalent for tar on older Pythons.

Source

Thrown at src/pip/_internal/utils/unpacking.py:300

    # PEP 706 added tarfile.data_filter, made tarfile extraction operations more secure.
    # This feature is fully supported from CPython 3.12 onward.
    for member in tar.getmembers():
        fn = member.name
        if leading:
            fn = split_leading_dir(fn)[1]
        path = os.path.join(location, fn)

        # The plain check rejects textual ".." escapes; resolving symlinks also
        # catches a later member redirected outside by an earlier member's
        # symlink (e.g. "link/../file").
        if not is_within_directory(location, path) or not is_within_directory(
            location, path, resolve_symlinks=True
        ):
            message = (
                "The tar file ({}) has a file ({}) trying to install "
                "outside target directory ({})"
            )
            raise InstallationError(message.format(filename, path, location))
        if member.isdir():
            ensure_dir(path)
        elif member.issym():
            # Reject symlinks resolving outside the destination, so a later
            # member cannot be written through them.
            target = os.path.join(os.path.dirname(path), member.linkname)
            if not is_within_directory(location, target, resolve_symlinks=True):
                message = (
                    "The tar file ({}) has a file ({}) trying to install "
                    "outside target directory ({})"
                )
                raise InstallationError(
                    message.format(filename, member.name, member.linkname)
                )
            if not is_symlink_target_in_tar(tar, member):
                message = (
                    "The tar file ({}) has a file ({}) trying to install "
                    "outside target directory ({})"

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Treat as a security event: inspect the tar members and reject the artifact.
  2. Upgrade Python to 3.12+ so pip can use the stricter data_filter path and give clearer messages.
  3. Re-download and verify the hash from a trusted index.
  4. Rebuild the archive without traversal/symlink escapes.

Example fix

// before
# python 3.10, archive has member 'sub/../../escape'
untar_file('pkg.tar.gz', '/target')

// after
# rebuild archive with normalized paths:
tar -czf pkg.tar.gz --transform 's,^,pkg/,' -C src .
Defensive patterns

Strategy: validation

Validate before calling

import tarfile, os
def members_within(path: str, dest: str) -> bool:
    dest = os.path.realpath(dest)
    with tarfile.open(path) as t:
        for m in t.getmembers():
            target = os.path.realpath(os.path.join(dest, m.name))
            if os.path.commonpath([dest, target]) != dest:
                return False
    return True

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Untarring a tar archive on Python <3.12 (no data_filter) where a member name contains `..` or where the resolved real path escapes the destination (is_within_directory with resolve_symlinks=True returns False).

Common situations: Installing an sdist with a malicious or buggy member path on Python 3.8–3.11; symlink-redirect escape crafted to defeat a plain textual check.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/c728b71955f496f3.json. Report an issue: GitHub.