vercel-labs/skills · critical · ArchiveValidationError

Archive contains unsafe path: ${entryPath}

Error message

Archive contains unsafe path: ${entryPath}

What it means

Thrown while extracting a downloaded archive when an entry's path fails archive path validation (validateArchivePath returns null). This is a zip-slip / path-traversal defense: the library refuses to extract entries whose normalized path is unsafe before writing anything to disk.

Source

Thrown at src/download-source.ts:185

}

async function extractTar(
  filePath: string,
  extractDir: string,
  limits: DownloadLimits
): Promise<void> {
  const state: ExtractState = { bytes: 0, entries: 0 };
  let validationError: ArchiveValidationError | undefined;

  await tar.x({
    strict: true,
    filter(entryPath, entry) {
      if (validationError) return false;

      try {
        const safePath = validateArchivePath(entryPath);
        if (safePath === null) {
          throw new ArchiveValidationError(`Archive contains unsafe path: ${entryPath}`);
        }

        const targetPath = join(extractDir, safePath);
        if (!isPathSafe(extractDir, targetPath)) {
          throw new ArchiveValidationError(`Archive contains unsafe path: ${entryPath}`);
        }

        incrementEntry(state, entry.size, limits);

        if (isTarEntryFile(entry)) {
          return true;
        }

        return getTarEntryType(entry) === 'Directory';
      } catch (error) {
        if (error instanceof ArchiveValidationError) {
          validationError = error;
          return false;

View on GitHub (pinned to 435076e789)

Solutions

  1. Inspect the downloaded archive locally (tar -tzf file.tar.gz) and identify the offending entry path
  2. If you control the archive, repackage it with relative, contained paths (cd dir && tar -czf ...)
  3. If the URL is third-party, verify the URL points to the intended artifact and is not redirected to an error page or hostile payload
  4. Do not bypass the check: this guard protects against arbitrary file overwrite (zip-slip)

Example fix

# before: archive contains entry like
#   ../../etc/cron.d/evil
cd my-skill && tar -czf ../safe.tar.gz .
# after: all entries are relative and contained (./SKILL.md, ./refs/...)
Defensive patterns

Strategy: validation

Validate before calling

import { validateArchivePath } from './download-source';
// or locally:
function isSafeEntry(p: string): boolean {
  if (!p || p.includes('\0')) return false;
  const norm = p.replace(/\\/g, '/');
  return !norm.split('/').some((s) => s === '..') && !norm.startsWith('/');
}
for (const entry of await listArchiveEntries(file)) {
  if (!isSafeEntry(entry)) throw new Error(`refusing archive: unsafe entry ${entry}`);
}

Type guard

function isArchiveValidationError(e: unknown): e is Error & { name: 'ArchiveValidationError' } {
  return e instanceof Error && e.name === 'ArchiveValidationError';
}

Try / catch

try {
  await downloadSource(url, limits);
} catch (e) {
  if (isArchiveValidationError(e)) {
    // hostile/corrupt archive — do NOT retry; drop the URL
    console.error(`Refusing unsafe archive from ${url}`);
    process.exitCode = 1;
  } else throw e;
}

Prevention

When it happens

Trigger: downloadSource() fetches a URL, the payload is detected as an archive, and tar.extract's filter callback encounters an entry whose path (e.g. '../evil.js', absolute paths, or NUL-prefixed names) does not validate via validateArchivePath. Also thrown if validationError was set by a previous entry.

Common situations: Downloading a maliciously or oddly crafted tarball/zip from an untrusted URL; archives created by tools that emit absolute entry names ('/usr/bin/x') or '..' segments; a truncated/corrupted download that garbles entry names.

Related errors


AI-assisted analysis of vercel-labs/skills@435076e789 (2026-08-28). Data as JSON: /api/errors/fab7f226af82a791. Report an issue: GitHub.