jdx/mise · error
vfox plugin archive contains a link or special file
Error message
vfox plugin archive contains a link or special file
What it means
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.
Source
Thrown at src/plugins/packslip.rs:212
ensure!(
artifact.requires.is_none(),
"vfox plugin artifacts must not declare host requirements"
);
ensure!(
matches!(artifact.format.as_deref(), Some("tar.gz" | "tgz")),
"vfox plugin artifacts currently require tar.gz format"
);
Ok(())
}
/// Reject links and special files before extraction, including links whose
/// targets might otherwise be resolved while unpacking a later archive entry.
pub(crate) fn validate_archive(path: &Path) -> Result<()> {
let reader = flate2::read::GzDecoder::new(std::fs::File::open(path)?);
let mut archive = jdx_tar::Archive::new(reader);
for entry in archive.entries()? {
let entry = entry?;
ensure!(
matches!(
entry.entry_type(),
jdx_tar::EntryType::File | jdx_tar::EntryType::Directory
),
"vfox plugin archive contains a link or special file"
);
let path = entry.path()?;
for component in path.components() {
if component == Component::CurDir {
continue;
}
let Component::Normal(name) = component else {
bail!("vfox plugin archive contains an unsafe path");
};
let name = name.to_string_lossy();
ensure!(
!name.contains(['\\', ':'])
&& !name.eq_ignore_ascii_case(".git")View on GitHub (pinned to afd2eddd3a)
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.
Example fix
// before: tar -czf plugin.tar.gz assets/ bin/extra -> bin/extra is a symlink // after tar --dereference -czf plugin.tar.gz assets/ bin/
Defensive patterns
Strategy: validation
Validate before calling
# shell: check a plugin tarball for links/special files before packaging tar -tvf plugin.tar.gz | grep -E '^(l|h|b|c|p)' && echo 'REJECTED: links/special files present' || echo 'OK'
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- vfox plugin archive contains an unsafe path
- vfox plugin archive contains a reserved or unsafe path
- vfox plugin archive must contain metadata.lua at its root
- brew-cask: invalid {kind} '{value}'
- {option}: '{name}' must be a plain file name (no path separa
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/c31b6e2dddbbf3d9.
Report an issue: GitHub.