denoland/deno · critical

refusing zip entry with traversal path: {}

Error message

refusing zip entry with traversal path: {}

What it means

Thrown while extracting a LAUFEY Windows .zip during a defence-in-depth re-check: even after `enclosed_name()` accepted the entry, the extractor independently verifies the returned relative path has no ParentDir (`..`) or RootDir components and bails if it does. It guards against a future regression or parser divergence in the zip crate's own checks.

Source

Thrown at cli/tools/desktop.rs:2282

    // helper has the same shape as the tar `unpack` we deliberately
    // avoided (no perm masking; no defence-in-depth against zip-slip
    // beyond the crate's own checks). Treat the archive as untrusted.
    for i in 0..archive.len() {
      let mut entry = archive.by_index(i)?;
      // `enclosed_name` rejects drive labels, absolute paths and `..`
      // components. Anything that fails this check is a zip-slip attempt
      // (or a legitimately weird archive we don't want to handle).
      let Some(rel_path) = entry.enclosed_name() else {
        bail!("refusing zip entry with unsafe path: {}", entry.name());
      };
      // Defence in depth — re-check the components ourselves.
      if rel_path.components().any(|c| {
        matches!(
          c,
          std::path::Component::ParentDir | std::path::Component::RootDir
        )
      }) {
        bail!(
          "refusing zip entry with traversal path: {}",
          rel_path.display()
        );
      }
      // Refuse symlinks: with prior entries already extracted, a
      // symlink-then-write pair is the standard zip-slip-via-symlink
      // escape, and LAUFEY Windows archives have no legitimate need for
      // them.
      if entry.is_symlink() {
        bail!(
          "refusing symlink entry in laufey archive: {}",
          rel_path.display()
        );
      }
      let dest_path = dest.join(&rel_path);
      if entry.is_dir() {
        std::fs::create_dir_all(&dest_path)?;
        continue;

View on GitHub (pinned to f7822238ca)

Solutions

  1. Treat the archive as malicious; do not attempt extraction with other tools.
  2. Verify via `unzip -l` and report the exact entry path, archive name, and URL to the deno/laufey maintainers.
  3. Clear the cache and retry once to rule out local corruption.
Defensive patterns

Strategy: try-catch

Try / catch

# Security abort in wrappers
if deno desktop main.ts 2>&1 | grep -q "refusing zip entry with traversal path"; then
  echo "SECURITY: traversal path in runtime zip — report upstream" >&2; exit 2
fi

Prevention

When it happens

Trigger: Any zip entry whose normalized components still contain `..` or a leading root after enclosed_name normalization — e.g. crafted names that slip past one checker but not the other, or corrupted central-directory data producing odd paths.

Common situations: Should never fire for legitimate archives; because the checksum gate passed, a hit signals a hostile or corrupted pinned release and warrants an upstream report.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/166d19a4f397fcf0. Report an issue: GitHub.