denoland/deno · critical

refusing tar entry that would unpack outside dest: {}

Error message

refusing tar entry that would unpack outside dest: {}

What it means

Thrown while extracting the LAUFEY runtime tar when `unpack_in(dest)` returns false for an entry — meaning the tar crate itself judged that the entry (including symlink/hardlink targets, which `unpack_in` containment-checks) would land outside the destination. It is the second layer of defence after the explicit `..`/root pre-check, and like that check it produces a hard error instead of the silent skip `unpack_in` would otherwise do.

Source

Thrown at cli/tools/desktop.rs:2238

      // `unpack_in`, since we want a hard error rather than the silent
      // skip that `unpack_in` does for a rejected entry.
      if entry_path.components().any(|c| {
        matches!(
          c,
          std::path::Component::ParentDir | std::path::Component::RootDir
        )
      }) {
        bail!(
          "refusing tar entry with traversal path: {}",
          entry_path.display()
        );
      }
      // `unpack_in` (vs. `unpack(absolute_path)`) makes tar enforce its
      // symlink + hardlink target containment too: a tar with entry A as
      // symlink `foo -> ../../etc` followed by entry B writing
      // `foo/passwd` would otherwise escape `dest`.
      if !entry.unpack_in(dest)? {
        bail!(
          "refusing tar entry that would unpack outside dest: {}",
          entry_path.display()
        );
      }
      #[cfg(unix)]
      {
        use std::os::unix::fs::PermissionsExt;
        let dest_path = dest.join(&entry_path);
        // `symlink_metadata` so we don't follow a just-extracted symlink
        // and chmod its target.
        if let Ok(meta) = std::fs::symlink_metadata(&dest_path)
          && meta.file_type().is_file()
        {
          // Was the entry executable? If so, mask to 0o755; otherwise 0o644.
          let mode = entry.header().mode().unwrap_or(0o644);
          let safe = if mode & 0o111 != 0 { 0o755 } else { 0o644 };
          let mut perms = meta.permissions();
          perms.set_mode(safe);

View on GitHub (pinned to f7822238ca)

Solutions

  1. Treat the archive as untrusted; do not extract it with other tools.
  2. Inspect with `tar -tvf` and check symlink targets (`ls -l` on extracted copy is NOT safe — read the listing instead).
  3. Clear the cache, retry once, and if it reproduces report the archive name/URL to the deno/laufey maintainers.
Defensive patterns

Strategy: try-catch

Try / catch

# Non-negotiable abort — do not fall back to other extractors
set +e; OUT="$(deno desktop main.ts 2>&1)"; RC=$?; set -e
if grep -q "refusing tar entry that would unpack outside dest" <<<"$OUT"; then
  echo "SECURITY: tar containment violated — quarantine cache and report" >&2; exit 2
fi
exit $RC

Prevention

When it happens

Trigger: A tar containing entry A as symlink `foo -> ../../elsewhere` followed by entry B writing through `foo/`; hardlink targets pointing to paths outside dest; any entry the tar crate's containment logic rejects as escaping the extraction root.

Common situations: Only with a crafted or badly damaged upstream release archive — the checksum verified beforehand, so a genuine hit means the pinned release is hostile or broken, or local cache corruption altered paths.

Related errors


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