{"record":{"id":"c31b6e2dddbbf3d9","repo":"jdx/mise","slug":"vfox-plugin-archive-contains-a-link-or-special-fil","errorCode":null,"errorMessage":"vfox plugin archive contains a link or special file","messagePattern":"vfox plugin archive contains a link or special file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugins/packslip.rs","lineNumber":212,"sourceCode":"    ensure!(\n        artifact.requires.is_none(),\n        \"vfox plugin artifacts must not declare host requirements\"\n    );\n    ensure!(\n        matches!(artifact.format.as_deref(), Some(\"tar.gz\" | \"tgz\")),\n        \"vfox plugin artifacts currently require tar.gz format\"\n    );\n    Ok(())\n}\n\n/// Reject links and special files before extraction, including links whose\n/// targets might otherwise be resolved while unpacking a later archive entry.\npub(crate) fn validate_archive(path: &Path) -> Result<()> {\n    let reader = flate2::read::GzDecoder::new(std::fs::File::open(path)?);\n    let mut archive = jdx_tar::Archive::new(reader);\n    for entry in archive.entries()? {\n        let entry = entry?;\n        ensure!(\n            matches!(\n                entry.entry_type(),\n                jdx_tar::EntryType::File | jdx_tar::EntryType::Directory\n            ),\n            \"vfox plugin archive contains a link or special file\"\n        );\n        let path = entry.path()?;\n        for component in path.components() {\n            if component == Component::CurDir {\n                continue;\n            }\n            let Component::Normal(name) = component else {\n                bail!(\"vfox plugin archive contains an unsafe path\");\n            };\n            let name = name.to_string_lossy();\n            ensure!(\n                !name.contains(['\\\\', ':'])\n                    && !name.eq_ignore_ascii_case(\".git\")","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/plugins/packslip.rs#L194-L230","documentation":"When installing or updating a vfox plugin delivered as a packslip-signed archive, mise inspects every tar entry before extraction. Any entry that is a symlink, hardlink, or special file (device, fifo, etc.) is rejected, because such entries can resolve to paths outside the extraction directory during unpacking. This is a security guard against archive-based path escape and file-overwrite attacks.","triggerScenarios":"Calling the vfox plugin install path in src/plugins/packslip.rs whose archive contains any tar entry with EntryType other than File or Directory — e.g. a symlinked binary or a hardlinked file inside the plugin tarball.","commonSituations":"Plugin authors build the archive with a tool that preserves symlinks (e.g. packaging a repo where bin/vfox-plugin points to the real script), or tar packaging picks up git worktree symlinks or special files from the source tree.","solutions":["Repackage the plugin archive so it contains only regular files and directories (resolve symlinks before tarring, e.g. `tar --dereference`).","Remove any symlinked or special files (sockets, fifos, devices) from the plugin source directory before archiving.","Verify the offending entry with `tar -tvf <archive> | grep -E '^l|^h'` to find links, then fix the packaging script."],"exampleFix":"// before: tar -czf plugin.tar.gz assets/ bin/extra -> bin/extra is a symlink\n// after\ntar --dereference -czf plugin.tar.gz assets/ bin/","handlingStrategy":"validation","validationCode":"# shell: check a plugin tarball for links/special files before packaging\ntar -tvf plugin.tar.gz | grep -E '^(l|h|b|c|p)' && echo 'REJECTED: links/special files present' || echo 'OK'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always package plugin archives with `tar --dereference` (or equivalent) so symlinks become regular files.","Add a CI step that lists archive entry types and fails on any non-file/non-directory entry.","Keep packaging scripts free of devices, fifos, and sockets in the staging directory."],"tags":["security","archive","validation","vfox"],"backgroundTag":"path-traversal-blocked","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}