{"record":{"id":"31b677caa8aed2f6","repo":"jdx/mise","slug":"vfox-plugin-archive-contains-a-reserved-or-unsafe","errorCode":null,"errorMessage":"vfox plugin archive contains a reserved or unsafe path","messagePattern":"vfox plugin archive contains a reserved or unsafe path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugins/packslip.rs","lineNumber":228,"sourceCode":"    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    );\n    Ok(())\n}\n","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/plugins/packslip.rs#L210-L246","documentation":"During archive validation for vfox plugins, each path component of every tar entry must be a normal component (no absolute paths, `..`, `.` traversal). Additionally, no component may contain backslashes or colons, or be named `.git` or the packslip state file (case-insensitively). This prevents path traversal and overwriting of mise's internal state during extraction.","triggerScenarios":"Installing a vfox plugin whose archive contains entries with absolute paths, `..` components, `\\` or `:` in a name, a `.git` directory, or a file matching the reserved state-file name.","commonSituations":"Archives built on Windows with backslash separators, tarballs that embed absolute paths (`tar -C /` mistakes), packaging scripts that accidentally include the `.git` directory, or malicious/tampered plugin archives.","solutions":["Repackage the archive with relative paths: `tar -czf plugin.tar.gz -C <staging-dir> .`.","Delete the `.git` directory (and any state-file-named files) before creating the archive.","Sanitize entry names to use forward slashes and remove traversal components in the packaging script.","If the archive is untrusted, do not attempt to work around the check — obtain the plugin from a trusted source."],"exampleFix":"// before: tar -czf plugin.tar.gz /home/me/plugin   (absolute paths, includes .git)\n// after\nrm -rf plugin/.git && tar -czf plugin.tar.gz -C /home/me/plugin .","handlingStrategy":"validation","validationCode":"# shell: reject absolute paths, .., backslashes, .git before installing\ntar -tzf plugin.tar.gz | grep -E '(^/|(^|/)\\.\\.(/|$)|\\\\|(^|/)\\.git(/|$))' && echo 'REJECTED: unsafe paths' || echo 'OK'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build archives from a staging directory with relative paths: `tar -C staging -czf out.tar.gz .`.","Exclude .git and any mise state-file names from packaging.","Run the path check in CI for every plugin release."],"tags":["security","archive","path-traversal","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"}