anthropics/skills · error · ValueError
unsafe archive entry: {m.filename!r}
Error message
unsafe archive entry: {m.filename!r} What it means
pptx copy of the zip-slip escape guard (same as error 7): safe_extract() resolves each member's destination path and raises if it falls outside the extraction directory — absolute entry names ('/etc/x') or '..' traversal. This blocks arbitrary-file overwrite during unpack of hostile or malformed archives.
Source
Thrown at skills/pptx/scripts/office/helpers/__init__.py:81
def part_text(data: bytes) -> str:
return data.decode("utf-8", "surrogateescape")
XML_SPACE = " \t\r\n"
def rendered_text(text: str, preserve: bool) -> str:
return text if preserve else text.strip(XML_SPACE)
def safe_extract(zf: zipfile.ZipFile, dest: Path) -> None:
dest = dest.resolve()
for m in zf.infolist():
if stat.S_ISLNK(m.external_attr >> 16):
raise ValueError(f"symlink archive entry not allowed: {m.filename!r}")
target = (dest / m.filename).resolve()
if not target.is_relative_to(dest):
raise ValueError(f"unsafe archive entry: {m.filename!r}")
zf.extract(m, dest)
def rezip(src_dir: Path, out_path: Path) -> None:
files = sorted(p for p in src_dir.rglob("*") if p.is_file())
ct = src_dir / "[Content_Types].xml"
fd, tmp_name = tempfile.mkstemp(
prefix=out_path.name + ".", suffix=".tmp", dir=out_path.parent
)
tmp_out = Path(tmp_name)
try:
with os.fdopen(fd, "wb") as fh:
with zipfile.ZipFile(fh, "w", zipfile.ZIP_DEFLATED) as zf:
if ct.exists():
zf.write(ct, ct.relative_to(src_dir), compress_type=zipfile.ZIP_STORED)
for f in files:
if f == ct:
continueView on GitHub (pinned to f6656c1256)
Solutions
- Reject/quarantine the file — the guard fired on a genuinely dangerous entry name.
- If the entry name is corruption fallout, rebuild the archive with clean relative names.
- Pre-scan archives for absolute or '..' names before processing.
- Restrict the pipeline to trusted .pptx sources.
Example fix
# pre-scan gate
import zipfile
def escapes(path):
with zipfile.ZipFile(path) as zf:
return any(n.startswith("/") or ".." in n.split("/") for n in zf.namelist())
if escapes(upload):
raise ValueError("reject archive with escaping entries") Defensive patterns
Strategy: validation
Validate before calling
import zipfile
def has_escaping_entries(path: str) -> bool:
with zipfile.ZipFile(path) as zf:
return any(n.startswith("/") or n.startswith("\\") or ".." in n.replace("\\", "/").split("/") for n in zf.namelist()) Try / catch
from office.helpers import safe_extract
try:
safe_extract(zf, dest)
except ValueError as e:
if "unsafe archive entry" in str(e):
quarantine(path) # zip-slip; no fallback extraction
else:
raise Prevention
- Pre-scan namelist() for absolute and '..' entry names.
- Extract to fresh temp dirs; never over live data directories.
- Never swap in raw extractall for untrusted archives.
When it happens
Trigger: Unpacking an archive containing an entry named '/tmp/evil' or '../../../home/u/.bashrc'; also non-OOXML zips mistakenly fed to the pptx unpack flow.
Common situations: Malicious uploads; corrupted zip central directories producing mangled names; archives from unreliable sources.
Related errors
- symlink archive entry not allowed: {m.filename!r}
- unsafe archive entry: {m.filename!r}
- symlink archive entry not allowed: {m.filename!r}
- unsafe archive entry: {m.filename!r}
- relationship target escapes the package: {target!r}
AI-assisted analysis of anthropics/skills@f6656c1256 (2026-08-14).
Data as JSON: /api/errors/0157a514acbdcdab.
Report an issue: GitHub.