crewAIInc/crewAI · critical · ValueError
Blocked path traversal attempt: {member.name!r}
Error message
Blocked path traversal attempt: {member.name!r} What it means
A defense inside `_safe_extract_tar`: for each tar member, `(dest / member.name).resolve()` must stay inside `dest.resolve()`. A member name like `../../etc/passwd` or an absolute path resolves outside the destination, so extraction is blocked with `ValueError` before `tf.extractall` runs. This protects `crewai skill install` from malicious registry archives (Zip Slip / tar traversal).
Source
Thrown at lib/cli/src/crewai_cli/skills/main.py:438
return None
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}"View on GitHub (pinned to 754d7323be)
Solutions
- Do not install the skill — report the malicious archive to the registry/org owners.
- If the failure is due to your own `skills/` dir being a symlink, install into a real directory (or make the symlink target the final location) and retry.
- Audit the archive manually before any override: download it and run `tar -tzf` to list member names.
Defensive patterns
Strategy: try-catch
Validate before calling
import tarfile
from pathlib import Path
def archive_is_safe(path: str, sample_dest: Path) -> bool:
dest = sample_dest.resolve()
with tarfile.open(path) as tf:
for m in tf.getmembers():
if not (dest / m.name).resolve().is_relative_to(dest):
return False
return True Try / catch
from crewai_cli.skills.main import _safe_extract_tar
import tarfile
try:
with tarfile.open(archive) as tf:
_safe_extract_tar(tf, dest)
except ValueError as exc:
if "path traversal" in str(exc):
quarantine_archive_and_report(ref) # never extract manually as a workaround
raise Prevention
- Never bypass or monkey-patch the safe-extract guards.
- Prefer `tar -tvzf` / `zipfile.namelist()` inspection of third-party archives before installing.
- Report archives with traversal paths to the registry maintainers; do not 'fix' by extracting by hand.
When it happens
Trigger: Installing a skill whose tarball contains a member such as `../../../.ssh/authorized_keys`, `/etc/cron.d/x`, or any path whose resolved location escapes the install directory. Note the resolve() also follows symlinks already on disk inside dest, so a previously-extracted symlink can make a later member trip this check.
Common situations: Compromised or hand-crafted skill archives served by (or injected into) the registry; MITM tampering with the download_url payload; benign edge cases where dest itself contains symlinks pointing outside (e.g. skills dir symlinked into a dotfiles repo).
Related errors
- Blocked unsupported tar member: {member.name!r}
- Blocked link target escaping destination: {member.name!r} ->
- Blocked path traversal attempt: {member!r}
- Invalid skill reference: org and name must be single, non-em
- [tool.crewai] definition must resolve inside the project roo
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/a0818932d299785f.
Report an issue: GitHub.