denoland/deno · critical

refusing zip entry with unsafe path: {}

Error message

refusing zip entry with unsafe path: {}

What it means

Thrown while extracting a LAUFEY Windows runtime .zip when `entry.enclosed_name()` returns None — the zip crate rejects names that are absolute, contain drive labels (C:\), or have `..` components, i.e. a zip-slip attempt (or a pathologically weird archive the tool refuses on principle). The extractor iterates entries manually instead of using archive.extract precisely to enforce this.

Source

Thrown at cli/tools/desktop.rs:2273

          let mut perms = meta.permissions();
          perms.set_mode(safe);
          let _ = std::fs::set_permissions(&dest_path, perms);
        }
      }
    }
  } else if name.ends_with(".zip") {
    let mut archive = zip::ZipArchive::new(std::io::Cursor::new(data))?;
    // Iterate entries manually rather than `archive.extract(dest)`: that
    // 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() {

View on GitHub (pinned to f7822238ca)

Solutions

  1. Do not extract the zip by hand or with other tools — keep it quarantined.
  2. List entries to confirm: `unzip -l <archive>` and look for absolute/`..` names.
  3. Clear the cache and retry once; if reproducible, report to the deno/laufey maintainers with the archive name.
Defensive patterns

Strategy: try-catch

Try / catch

# Abort and report; never retry with permissive tools
if deno desktop main.ts 2>&1 | grep -q "refusing zip entry with unsafe path"; then
  echo "SECURITY: zip-slip attempt in runtime archive — report upstream" >&2; exit 2
fi

Prevention

When it happens

Trigger: A zip entry named `../evil.dll`, `C:\Windows\system32\x.dll`, or `/abs/path`; archives built with non-normalized paths (`a/../b`); an upstream release accidentally packaged with absolute paths.

Common situations: Practically unreachable in normal use because the archive was SHA-256-verified against the pinned release first — a hit means the pinned release itself is malicious or mis-packaged, or the cache was corrupted post-verification.

Related errors


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