{"record":{"id":"57007822f0f1734b","repo":"astrid-runtime/astrid","slug":"durable-capsule-archive-contains-a-link-or-special","errorCode":null,"errorMessage":"durable capsule archive contains a link or special file","messagePattern":"durable capsule archive contains a link or special file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":377,"sourceCode":"    let mut files = std::collections::BTreeMap::new();\n    let mut directories = std::collections::BTreeSet::new();\n    for entry in archive.entries().context(\"read durable capsule archive\")? {\n        let mut entry = entry.context(\"read durable capsule archive entry\")?;\n        let path = entry.path().context(\"read durable capsule archive path\")?;\n        if path.is_absolute()\n            || path.components().any(|component| {\n                matches!(\n                    component,\n                    std::path::Component::ParentDir | std::path::Component::RootDir\n                )\n            })\n        {\n            bail!(\"durable capsule archive contains unsafe path\");\n        }\n\n        let entry_type = entry.header().entry_type();\n        if !entry_type.is_dir() && !entry_type.is_file() {\n            bail!(\"durable capsule archive contains a link or special file\");\n        }\n\n        let name = path\n            .to_str()\n            .ok_or_else(|| anyhow::anyhow!(\"durable capsule archive path is not UTF-8\"))?\n            .replace('\\\\', \"/\");\n        if files.contains_key(&name) || directories.contains(&name) {\n            bail!(\"durable capsule archive contains duplicate path {name}\");\n        }\n        if entry_type.is_dir() {\n            if !directories.insert(name) {\n                bail!(\"durable capsule archive contains duplicate directory path\");\n            }\n            continue;\n        }\n\n        let mut bytes = Vec::new();\n        entry","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L359-L395","documentation":"read_archive_files found a tar entry whose type is neither a regular file nor a directory — e.g. a symlink, hardlink, fifo, or device node. The library refuses such entries because links and special files can redirect reads/writes outside the capsule content and have no place in a deterministic capsule archive.","triggerScenarios":"read_archive_files on an archive containing symlink/hardlink/device entries; entry_type().is_dir() and is_file() both false triggers the bail.","commonSituations":"Source tree containing symlinks (node_modules links, docs symlinks) archived with tar's default link preservation; packaging on Unix where symlinks are common; archives produced by tools that include metadata nodes.","solutions":["Rebuild the archive dereferencing symlinks (--dereference / follow-links) so only regular files are stored.","Remove symlinks/special files from the source tree before packaging (vendor or inline their content).","Configure the archiver to skip non-regular entry types.","Reject the archive and ask the publisher to fix their packaging."],"exampleFix":"// before: tar preserves symlinks\ntar -czf capsule.tgz -C src .            # keeps symlinks\n// after: dereference links when archiving\ntar -czf capsule.tgz -C src --dereference .","handlingStrategy":"validation","validationCode":"let entry_type = entry.header().entry_type();\nif !entry_type.is_dir() && !entry_type.is_file() {\n    return Err(\"archive entry is a link or special file\");\n}","typeGuard":"fn is_regular_entry(h: &tar::Header) -> bool {\n    let t = h.entry_type();\n    t.is_file() || t.is_dir()\n}","tryCatchPattern":"match read_verified_durable_package_for_owner(&store, owner, id).await {\n    Ok(pkg) => pkg,\n    Err(e) if e.to_string().contains(\"link or special file\") => {\n        // rebuild archive with --dereference or without symlinks\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Archive with --dereference (or follow symlinks in code) so only regular files are stored.","Keep capsule source trees symlink-free; vendor linked content.","Inspect the archive (tar -tvf) for 'l' type entries before publishing."],"tags":["archive","tar","symlink","security"],"backgroundTag":"unsupported-operation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}