crewAIInc/crewAI · critical · ValueError

Blocked unsupported tar member: {member.name!r}

Error message

Blocked unsupported tar member: {member.name!r}

What it means

`_safe_extract_tar` only permits regular files, directories, symlinks, and hardlinks. Any other tar member type — device nodes (character/block), FIFOs — is rejected with `ValueError` before extraction, because such members have no legitimate purpose in a skill archive and are a classic tar-bomb primitive.

Source

Thrown at lib/cli/src/crewai_cli/skills/main.py:440

def _safe_extractall(tf: tarfile.TarFile, dest: Path) -> None:
    """Path-traversal-safe extraction for Python versions without tar filters.

    Validates both the member's own path and, for symlink/hardlink members,
    the link target. Without the link-target check a malicious archive can
    plant a symlink that escapes ``dest`` (e.g. ``link -> /home/user/.ssh``)
    followed by a regular member written *through* that link
    (``link/authorized_keys``), escaping ``dest`` even though every member
    name resolves inside it. This mirrors the protection that
    ``tarfile.extractall(..., filter="data")`` provides when available.
    """
    dest_resolved = dest.resolve()
    for member in tf.getmembers():
        member_path = (dest / member.name).resolve()
        if not member_path.is_relative_to(dest_resolved):
            raise ValueError(f"Blocked path traversal attempt: {member.name!r}")
        if not (member.isfile() or member.isdir() or member.issym() or member.islnk()):
            raise ValueError(f"Blocked unsupported tar member: {member.name!r}")
        if member.issym() or member.islnk():
            link_target = member.linkname
            # Absolute link targets always escape the destination.
            if os.path.isabs(link_target):
                raise ValueError(
                    f"Blocked link target escaping destination: "
                    f"{member.name!r} -> {link_target!r}"
                )
            # Hardlink names are relative to the archive root; symlink
            # targets are relative to the member's own directory.
            anchor = dest if member.islnk() else (dest / member.name).parent
            resolved_target = (anchor / link_target).resolve()
            if not resolved_target.is_relative_to(dest_resolved):
                raise ValueError(
                    f"Blocked link target escaping destination: "
                    f"{member.name!r} -> {link_target!r}"
                )
    tf.extractall(dest)  # noqa: S202

View on GitHub (pinned to 754d7323be)

Solutions

  1. Do not install this archive; report it to the registry maintainers if it came from the registry.
  2. If you authored the skill, rebuild the tarball from a clean directory containing only regular files/dirs (e.g. re-run `crewai skill publish` from a sanitized folder).
  3. Inspect with `tar -tvzf archive.tar.gz` to find the offending member.
Defensive patterns

Strategy: try-catch

Validate before calling

import tarfile

def only_supported_members(path: str) -> bool:
    with tarfile.open(path) as tf:
        return all(m.isfile() or m.isdir() or m.issym() or m.islnk() for m in tf.getmembers())

Try / catch

try:
    _safe_extract_tar(tf, dest)
except ValueError as exc:
    if "unsupported tar member" in str(exc):
        log_security_event(f"non-file member in skill archive: {exc}")
        delete_archive()
    raise

Prevention

When it happens

Trigger: Installing a skill archive containing a device member (e.g. `/dev/null` style char device created by a poorly configured `tar -czf /dev/...`) or a FIFO. The `not (member.isfile() or member.isdir() or member.issym() or member.islnk())` branch fires.

Common situations: A skill author accidentally tarred device files or special nodes from their filesystem; deliberately malicious archives trying to plant devices; rare GNU tar options that store sparseness/special types the check does not whitelist.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/827c1cd8f6bf4195. Report an issue: GitHub.