{"record":{"id":"166d19a4f397fcf0","repo":"denoland/deno","slug":"refusing-zip-entry-with-traversal-path","errorCode":null,"errorMessage":"refusing zip entry with traversal path: {}","messagePattern":"refusing zip entry with traversal path: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"cli/tools/desktop.rs","lineNumber":2282,"sourceCode":"    // helper has the same shape as the tar `unpack` we deliberately\n    // avoided (no perm masking; no defence-in-depth against zip-slip\n    // beyond the crate's own checks). Treat the archive as untrusted.\n    for i in 0..archive.len() {\n      let mut entry = archive.by_index(i)?;\n      // `enclosed_name` rejects drive labels, absolute paths and `..`\n      // components. Anything that fails this check is a zip-slip attempt\n      // (or a legitimately weird archive we don't want to handle).\n      let Some(rel_path) = entry.enclosed_name() else {\n        bail!(\"refusing zip entry with unsafe path: {}\", entry.name());\n      };\n      // Defence in depth — re-check the components ourselves.\n      if rel_path.components().any(|c| {\n        matches!(\n          c,\n          std::path::Component::ParentDir | std::path::Component::RootDir\n        )\n      }) {\n        bail!(\n          \"refusing zip entry with traversal path: {}\",\n          rel_path.display()\n        );\n      }\n      // Refuse symlinks: with prior entries already extracted, a\n      // symlink-then-write pair is the standard zip-slip-via-symlink\n      // escape, and LAUFEY Windows archives have no legitimate need for\n      // them.\n      if entry.is_symlink() {\n        bail!(\n          \"refusing symlink entry in laufey archive: {}\",\n          rel_path.display()\n        );\n      }\n      let dest_path = dest.join(&rel_path);\n      if entry.is_dir() {\n        std::fs::create_dir_all(&dest_path)?;\n        continue;","sourceCodeStart":2264,"sourceCodeEnd":2300,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L2264-L2300","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat the archive as malicious; do not attempt extraction with other tools.","Verify via `unzip -l` and report the exact entry path, archive name, and URL to the deno/laufey maintainers.","Clear the cache and retry once to rule out local corruption."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"# Security abort in wrappers\nif deno desktop main.ts 2>&1 | grep -q \"refusing zip entry with traversal path\"; then\n  echo \"SECURITY: traversal path in runtime zip — report upstream\" >&2; exit 2\nfi","preventionTips":["Never weaken the double check even though the zip crate already filters.","Reproduce once after clearing the cache to separate corruption from a hostile release.","Include the entry path and archive URL in any upstream report."],"tags":["desktop","laufey","zip","zip-slip","path-traversal","security"],"backgroundTag":"zip-slip-path-traversal","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T04:17:50.494Z"}