python-poetry/poetry · error · ValueError

Refusing hardlink {member.name}: target {member.linkname} ou

Error message

Refusing hardlink {member.name}: target {member.linkname} outside {dest}

What it means

Raised by extractall during manual tar validation for hardlink members (member.islnk()). Unlike symlinks, the hardlink target is resolved relative to dest itself (os.path.abspath(dest / member.linkname)) and rejected if it is not under dest. This prevents a hardlink member from binding to a file outside the extraction tree.

Source

Thrown at src/poetry/utils/helpers.py:326

                    member_path = Path(os.path.abspath(dest / member.name))
                    if not member_path.is_relative_to(dest):
                        raise ValueError(
                            f"Refusing to extract {member.name}: "
                            f"would write outside {dest}"
                        )
                    if member.issym():
                        link_target = Path(
                            os.path.abspath(member_path.parent / member.linkname)
                        )
                        if not link_target.is_relative_to(dest):
                            raise ValueError(
                                f"Refusing symlink {member.name}: "
                                f"target {member.linkname} outside {dest}"
                            )
                    elif member.islnk():
                        link_target = Path(os.path.abspath(dest / member.linkname))
                        if not link_target.is_relative_to(dest):
                            raise ValueError(
                                f"Refusing hardlink {member.name}: "
                                f"target {member.linkname} outside {dest}"
                            )
                    safe_members.append(member)
                archive.extractall(dest, members=safe_members)


_DEPRECATED_DOWNLOAD_EXPORTS = {
    "Downloader",
    "download_file",
    "HTTPRangeRequestSupportedError",
}


def __getattr__(name: str) -> object:
    if name in _DEPRECATED_DOWNLOAD_EXPORTS:
        warnings.warn(
            f"Importing `{name}` from `poetry.utils.helpers` is deprecated;"

View on GitHub (pinned to 92b74dcfe3)

Solutions

  1. Inspect `tar -tvf` for hardlink members (type '1') and strip/rebuild entries that point outside the root.
  2. Use a Python version with a functional tarfile.data_filter.
  3. Re-acquire the archive from a trusted source.
  4. Extract in an isolated environment to assess risk before touching real paths.

Example fix

# before
extractall(Path('pkg.tar'), dest, zip=False)
# after - vet hardlink targets
import tarfile, os
from pathlib import Path
with tarfile.open('pkg.tar') as a:
    dest = Path(os.path.abspath(dest))
    for m in a.getmembers():
        if m.islnk():
            t = Path(os.path.abspath(dest / m.linkname))
            if not t.is_relative_to(dest):
                raise ValueError(f'hardlink escapes dest: {m.name}')
    a.extractall(dest)
Defensive patterns

Strategy: validation

Validate before calling

import os, tarfile
from pathlib import Path

def tar_hardlinks_safe(source: Path, dest: Path) -> bool:
    dest = Path(os.path.abspath(dest))
    with tarfile.open(source) as a:
        for m in a.getmembers():
            if m.islnk():
                t = Path(os.path.abspath(dest / m.linkname))
                if not t.is_relative_to(dest):
                    return False
    return True

Try / catch

try:
    extractall(source, dest, zip=False)
except ValueError as e:
    # hardlink escaped dest; do not extract
    raise

Prevention

When it happens

Trigger: Extracting a tar (zip=False, no working data_filter) containing a hardlink member whose linkname, joined onto dest, resolves outside dest, e.g. a hardlink to '../../etc/shadow'.

Common situations: Crafted tarballs using hardlinks to expose host files; toolchain output that records hardlinks against absolute paths; Python pinned to a blocklisted patch level.

Related errors


AI-assisted analysis of python-poetry/poetry@92b74dcfe3 (2026-08-04). Data as JSON: /data/errors/fca5ebd2ed629cba.json. Report an issue: GitHub.