{"record":{"id":"cee0f8651721a764","repo":"crewAIInc/crewAI","slug":"blocked-path-traversal-attempt-member-r","errorCode":null,"errorMessage":"Blocked path traversal attempt: {member!r}","messagePattern":"Blocked path traversal attempt: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"lib/cli/src/crewai_cli/skills/main.py","lineNumber":467,"sourceCode":"            # Hardlink names are relative to the archive root; symlink\n            # targets are relative to the member's own directory.\n            anchor = dest if member.islnk() else (dest / member.name).parent\n            resolved_target = (anchor / link_target).resolve()\n            if not resolved_target.is_relative_to(dest_resolved):\n                raise ValueError(\n                    f\"Blocked link target escaping destination: \"\n                    f\"{member.name!r} -> {link_target!r}\"\n                )\n    tf.extractall(dest)  # noqa: S202\n\n\ndef _safe_extract_zip(zf: zipfile.ZipFile, dest: Path) -> None:\n    \"\"\"Path-traversal-safe ZIP extraction.\"\"\"\n    dest_resolved = dest.resolve()\n    for member in zf.namelist():\n        member_path = (dest / member).resolve()\n        if not member_path.is_relative_to(dest_resolved):\n            raise ValueError(f\"Blocked path traversal attempt: {member!r}\")\n    zf.extractall(dest)  # noqa: S202\n","sourceCodeStart":449,"sourceCodeEnd":469,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/cli/src/crewai_cli/skills/main.py#L449-L469","documentation":"The ZIP counterpart of the tar checks: `_safe_extract_zip` resolves every entry name from `zf.namelist()` against the destination and raises `ValueError` if any resolves outside it, blocking Zip Slip attacks before `extractall`. It guards the ZIP branch of skill installation/unpacking.","triggerScenarios":"Installing a skill delivered as a ZIP containing entries like `../../.bashrc`, `../evil.txt`, or absolute paths such as `/tmp/x`; any `member_path.is_relative_to(dest_resolved)` violation over `zf.namelist()`.","commonSituations":"Malicious or tampered ZIP payloads from a registry download_url; ZIPs produced by tools that store absolute entry names; archives that combine `..` segments with backslashes on Windows-normalized names.","solutions":["Do not install the skill; report the archive to the registry maintainers.","Inspect the payload first: `python -c \"import zipfile; print('\\n'.join(zipfile.ZipFile('s.zip').namelist()))\"` to see offending entries.","If you authored the skill, rebuild the ZIP from a clean directory with relative paths only and republish."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import zipfile\nfrom pathlib import Path\n\ndef zip_entries_safe(path: str, dest: Path) -> bool:\n    dest_r = dest.resolve()\n    with zipfile.ZipFile(path) as zf:\n        return all((dest / n).resolve().is_relative_to(dest_r) for n in zf.namelist())","typeGuard":null,"tryCatchPattern":"from crewai_cli.skills.main import _safe_extract_zip\n\ntry:\n    with zipfile.ZipFile(archive) as zf:\n        _safe_extract_zip(zf, dest)\nexcept ValueError as exc:\n    if \"path traversal\" in str(exc):\n        report_and_delete(archive)  # do not extract manually\n    raise","preventionTips":["Only install skills from registries/orgs you trust; traversal entries indicate tampering.","Inspect `zipfile.ZipFile(...).namelist()` for `..` or absolute entries before installing third-party skills.","When building ZIPs, `cd` into the skill directory before zipping so all entries are relative."],"tags":["security","zip","path-traversal","archive","install","skill"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}