{"record":{"id":"e3c6278d594b6e8c","repo":"denoland/deno","slug":"refusing-symlink-entry-in-laufey-archive","errorCode":null,"errorMessage":"refusing symlink entry in laufey archive: {}","messagePattern":"refusing symlink entry in laufey archive: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"cli/tools/desktop.rs","lineNumber":2292,"sourceCode":"      };\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;\n      }\n      if let Some(parent) = dest_path.parent() {\n        std::fs::create_dir_all(parent)?;\n      }\n      let mut out = std::fs::File::create(&dest_path)?;\n      std::io::copy(&mut entry, &mut out)?;\n      #[cfg(unix)]\n      {\n        use std::os::unix::fs::PermissionsExt;\n        // Mask to 0o755 / 0o644 — same policy as the tar branch.","sourceCodeStart":2274,"sourceCodeEnd":2310,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L2274-L2310","documentation":"Thrown while extracting a LAUFEY Windows .zip when an entry is a symlink. Symlinks are refused categorically because a symlink-then-write pair inside one archive is the standard zip-slip-via-symlink escape (extract `foo -> ../../etc`, then write `foo/passwd`), and the LAUFEY Windows archives have no legitimate need for symlinks.","triggerScenarios":"A zip containing a symlink entry (unix mode bits with S_IFLNK, or Windows symlink attributes) followed by a file that writes through it; a repackaged upstream archive that accidentally included symlinks from a Unix build.","commonSituations":"Not reachable via normal releases (checksum-gated); indicates a crafted archive, a compromised/mis-packaged release, or local cache corruption.","solutions":["Do not extract with alternative tools that permit symlinks; keep the archive quarantined.","Confirm the entry: `unzip -l` / `zipinfo` shows link entries and their targets.","Clear the cache and retry once; if it reproduces, report to the deno/laufey maintainers."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"# Security abort; do not extract symlinks manually\nif deno desktop main.ts 2>&1 | grep -q \"refusing symlink entry in laufey archive\"; then\n  echo \"SECURITY: symlink in runtime zip — report upstream\" >&2; exit 2\nfi","preventionTips":["Do not hand-extract the runtime zip — the symlink refusal blocks a write-through-symlink escape.","Keep the cache directory under your control; untrusted local users modifying it defeats the pipeline.","Report any symlink hit with the entry name from the message."],"tags":["desktop","laufey","zip","symlink","path-traversal","security"],"backgroundTag":"symlink-traversal-attack","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T04:17:50.494Z"}