{"record":{"id":"061ccf2874da5b32","repo":"jdx/mise","slug":"vfox-plugin-archive-contains-an-unsafe-path","errorCode":null,"errorMessage":"vfox plugin archive contains an unsafe path","messagePattern":"vfox plugin archive contains an unsafe path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/plugins/packslip.rs","lineNumber":225,"sourceCode":"pub(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\")\n                    && !name.eq_ignore_ascii_case(STATE_FILE),\n                \"vfox plugin archive contains a reserved or unsafe path\"\n            );\n        }\n    }\n    Ok(())\n}\n\npub(crate) fn validate_layout(path: &Path) -> Result<()> {\n    ensure!(\n        path.join(\"metadata.lua\").is_file(),\n        \"vfox plugin archive must contain metadata.lua at its root\"\n    );","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/plugins/packslip.rs#L207-L243","documentation":"When validating a vfox plugin archive before extraction, mise checks every entry path for unsafe components. Any path component that is not Normal (e.g. `..`, absolute/root, prefix components), or a name containing backslashes/colons or a `.git`/state-file name, causes the archive to be rejected as containing an unsafe (path-traversal) path.","triggerScenarios":"Installing/updating a vfox plugin whose archive contains entries with `..` components, absolute paths, Windows drive/prefix components, or names with `\\` or `:` or a top-level `.git`/state file — caught during validate_archive in packslip.rs.","commonSituations":"A maliciously crafted or compromised plugin archive attempting zip-slip traversal; archives built on Windows leaking drive-letter or backslash paths; archives that accidentally include their .git directory; corrupted archives with mangled entry names.","solutions":["Obtain the plugin archive from the trusted upstream source / re-download it; treat this as a potentially malicious archive and don't extract it manually elsewhere.","Repackage the archive correctly: build it with relative paths from the plugin root, excluding .git, using forward slashes and no `..` or drive-letter components.","Report the offending plugin/archive to its maintainer if the unsafe paths appear in the official release."],"exampleFix":"// before (repackaging includes traversal + .git)\ntar -czf plugin.tar.gz ../my-plugin/.git ../my-plugin\n// after\ncd my-plugin && tar -czf ../plugin.tar.gz --exclude=.git .","handlingStrategy":"try-catch","validationCode":"import std::path::{Path, Component};\nfn archive_entries_look_safe(entries: &[String]) -> bool {\n    entries.iter().all(|e| {\n        Path::new(e).components().all(|c| matches!(c, Component::Normal(n) if !n.to_string_lossy().contains(['\\\\',':']) && !n.eq_ignore_ascii_case(\".git\")))\n    })\n}","typeGuard":null,"tryCatchPattern":"match install_vfox_plugin(archive) {\n    Err(e) if e.to_string().contains(\"unsafe path\") => {\n        eprintln!(\"archive rejected as unsafe (possible path traversal); use a trusted source\");\n        // do not extract manually\n    }\n    r => r,\n}","preventionTips":["Only install vfox plugins from trusted, verified sources.","Repackage archives with relative paths, forward slashes, and .git excluded.","Verify archive integrity (checksums/signatures) before installing.","Treat any 'unsafe path' rejection as a security signal and inspect the archive, not just retry."],"tags":["security","archive","path-traversal","vfox","plugins"],"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-14T00:17:10.932Z"}