vercel-labs/skills · critical · ArchiveValidationError

Archive contains unsafe path: ${path}

Error message

Archive contains unsafe path: ${path}

What it means

Before writing each extracted zip entry, extractZip joins the entry path with extractDir and validates it with isPathSafe; if the resolved target escapes the extraction directory, it throws ArchiveValidationError. This is the defense against Zip Slip (CVE-2018-12689-style path traversal), where an entry named ../../.bashrc would otherwise overwrite files outside the extraction root.

Source

Thrown at src/download-source.ts:147

  } catch {
    return false;
  }
}

async function extractZip(
  filePath: string,
  extractDir: string,
  limits: DownloadLimits
): Promise<void> {
  const files = readZipArchive(await readFile(filePath), {
    maxExtractedBytes: limits.extractMaxBytes,
    maxEntries: limits.extractMaxFiles,
  });

  for (const [path, contents] of files) {
    const targetPath = join(extractDir, path);
    if (!isPathSafe(extractDir, targetPath)) {
      throw new ArchiveValidationError(`Archive contains unsafe path: ${path}`);
    }

    await mkdir(dirname(targetPath), { recursive: true });
    await writeFile(targetPath, contents);
  }
}

function getTarEntryType(entry: tar.ReadEntry | Stats): string {
  if (entry instanceof tar.ReadEntry) {
    return entry.type;
  }
  if (entry.isFile()) return 'File';
  if (entry.isDirectory()) return 'Directory';
  return '';
}

function isTarEntryFile(entry: tar.ReadEntry | Stats): boolean {
  const type = getTarEntryType(entry);

View on GitHub (pinned to 435076e789)

Solutions

  1. Do not extract the archive — treat it as hostile and remove it
  2. Inspect the offending entries: unzip -l archive.zip | grep -E '(^|/)(\.\.|/)\.' or list entries for ../ patterns
  3. Obtain the archive from a trusted source or repackage its safe contents yourself
  4. Report the malicious archive to wherever it was hosted

Example fix

# before
skills add https://untrusted.example/skills.zip  # unsafe path: ../../.ssh/authorized_keys

# after
# inspect first
unzip -l untrusted-skills.zip
# then install from the canonical, trusted repo instead
skills add verified-org/agent-skills
Defensive patterns

Strategy: validation

Validate before calling

// Reject archives containing traversal-style entries before extracting
import { createReadStream } from 'node:fs';
import * as unzipper from 'unzipper';
async function assertNoUnsafeEntries(path: string): Promise<void> {
  const dir = await unzipper.Open.file(path);
  for (const f of dir.files) {
    const norm = f.path.replace(/\\/g, '/');
    if (norm.startsWith('/') || /^[A-Za-z]:/.test(norm) || norm.split('/').includes('..')) {
      throw new Error(`Unsafe entry: ${f.path}`);
    }
  }
}

Type guard

function isSafeEntryPath(entryPath: string): boolean {
  const norm = entryPath.replace(/\\/g, '/');
  if (norm.startsWith('/') || /^[A-Za-z]:/.test(norm)) return false;
  return !norm.split('/').includes('..');
}

Try / catch

try {
  await extractArchive(file);
} catch (err) {
  if (err instanceof ArchiveValidationError && err.message.startsWith('Archive contains unsafe path')) {
    // SECURITY event: quarantine the archive, do NOT extract with other tools blindly
  } else throw err;
}

Prevention

When it happens

Trigger: Extracting an archive containing entries with absolute paths (/etc/...), drive letters (C:\...), ../ sequences, or symlink-style traversal that resolves outside extractDir. Usually seen with maliciously crafted archives or buggy packaging scripts that preserve absolute paths.

Common situations: Downloading skill archives from untrusted third parties, archives created with absolute paths by misconfigured build tools, or penetration-test payloads deliberately containing traversal entries.

Related errors


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