agentscope-ai/agentscope · error · Exception

unsafe archive member: ' + name

Error message

unsafe archive member: ' + name

What it means

Same Zip-Slip guard as the tar shim, but for the zip/tar archive-expansion shim: each member name (and symlink/hardlink targets) must realpath inside the destination. The member name that failed is included in the message.

Source

Thrown at src/agentscope/workspace/_base.py:193

    "finally:\n"
    "    tf.close()\n"
    "os.unlink(src)\n"
)

#: Expands an archive inside the sandbox. Runs there rather than on the
#: server so a traversing or bomb-sized archive detonates in the
#: isolated environment. Argv: src, dst, format, max extracted bytes.
_EXTRACT_ARCHIVE_SHIM = (
    "import os, sys, tarfile, zipfile\n"
    "src, dst, fmt, limit = sys.argv[1:5]\n"
    "limit = int(limit)\n"
    "os.makedirs(dst, exist_ok=True)\n"
    "dst_real = os.path.realpath(dst)\n"
    "def check(name):\n"
    "    target = os.path.realpath(os.path.join(dst, name))\n"
    "    if not (target == dst_real"
    " or target.startswith(dst_real + os.sep)):\n"
    "        raise Exception('unsafe archive member: ' + name)\n"
    "if fmt == 'zip':\n"
    "    ar = zipfile.ZipFile(src)\n"
    "    members = ar.infolist()\n"
    "    total = sum(m.file_size for m in members)\n"
    "    names = [m.filename for m in members]\n"
    "else:\n"
    "    ar = tarfile.open(src)\n"
    "    members = ar.getmembers()\n"
    "    total = sum(m.size for m in members)\n"
    "    names = [m.name for m in members]\n"
    "    for m in members:\n"
    "        if m.issym() or m.islnk():\n"
    "            check(os.path.join(os.path.dirname(m.name), m.linkname))\n"
    "try:\n"
    "    if total > limit:\n"
    "        raise Exception('archive expands to %d bytes, limit is %d'\n"
    "                        % (total, limit))\n"
    "    for name in names:\n"

View on GitHub (pinned to e90f1c7592)

Solutions

  1. List the archive contents (`unzip -l skill.zip` / `tar -tvf`) and find the offending entry named in the message
  2. Repack with relative, forward-slash member names
  3. Verify no entry is a symlink pointing outside the archive root
  4. Only install archives from trusted sources

Example fix

# before: entry 'C:\\skills\\my\\SKILL.md' or '../../x'
# after: repack so entries are relative
zip skill.zip SKILL.md assets/ -r  # run from inside the skill dir
Defensive patterns

Strategy: validation

Validate before calling

import zipfile, tarfile

def archive_members_safe(path: str) -> bool:
    if path.endswith('.zip'):
        names = [m.filename for m in zipfile.ZipFile(path).infolist()]
    else:
        names = [m.name for m in tarfile.open(path)]
    for n in names:
        if n.startswith(('/', '\\')) or '..' in n.replace('\\', '/').split('/'):
            return False
    return True

Try / catch

try:
    await ws.add_skill_archive(path, name)
except ValueError as e:
    if 'unsafe archive member' in str(e):
        quarantine(path)  # treat as hostile input
    raise

Prevention

When it happens

Trigger: add_skill_archive with a zip (or tar) whose entries have names resolving outside the extraction dir — absolute names, ../ segments, or link members whose linkname escapes.

Common situations: Zips created on Windows with absolute paths or backslash traversal, downloaded skill archives containing symlinks out of the root, repackaged content from untrusted marketplaces.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/d0e7166409284256. Report an issue: GitHub.