astrid-runtime/astrid · critical
malicious shuttle detected: invalid path '{}'
Error message
malicious shuttle detected: invalid path '{}' What it means
`unpack` validates each archive entry's path before writing anything to disk, rejecting absolute paths and any path containing `..` components. This is a zip-slip/path-traversal defense: a crafted `.shuttle` could otherwise write files outside the extraction directory.
Source
Thrown at crates/astrid-cli/src/commands/distro/shuttle.rs:188
let tar = flate2::read::GzDecoder::new(tar_gz);
let mut archive = tar::Archive::new(tar);
for entry in archive
.entries()
.context("failed to read shuttle entries (truncated or not a gzip tar?)")?
{
let mut entry = entry.context("failed to read shuttle entry (truncated archive?)")?;
let entry_path = entry
.path()
.context("invalid path in shuttle")?
.into_owned();
if entry_path.is_absolute()
|| entry_path
.components()
.any(|c| matches!(c, Component::ParentDir))
{
bail!(
"malicious shuttle detected: invalid path '{}'",
entry_path.display()
);
}
let et = entry.header().entry_type();
if et.is_symlink() || et.is_hard_link() {
bail!(
"malicious shuttle detected: links are not allowed ('{}')",
entry_path.display()
);
}
// Skip directory entries — parents are created as needed below.
if et.is_dir() {
continue;
}
// Everything that survives to here must be an ordinary file. Device
// nodes, FIFOs, sockets, and any other special entry type areView on GitHub (pinned to affd8760f4)
Solutions
- Do not install this shuttle — treat it as malicious or corrupt and obtain it from a trusted source.
- Re-pack the capsule with relative, `..`-free paths using the project's `pack` command.
- If you control the producer, fix the packing code to sanitize member paths (e.g. strip leading `/` and reject `..`).
Defensive patterns
Strategy: validation
Validate before calling
fn entry_path_is_safe(p: &str) -> bool {
let path = std::path::Path::new(p);
!path.is_absolute()
&& !path.components().any(|c| matches!(c, std::path::Component::ParentDir))
} Type guard
fn is_safe_member(p: &Path) -> bool {
p.is_relative() && !p.components().any(|c| matches!(c, Component::ParentDir))
} Try / catch
if let Err(e) = shuttle::unpack(archive, dest) {
if e.to_string().contains("invalid path") {
eprintln!("rejecting malicious archive: {e}");
// do not retry; discard the archive
}
} Prevention
- Only install capsules from trusted, signature-verified sources
- Inspect archive member names (`tar tf`) before unpacking unknown archives
- Prefer the official pack command over hand-built tars
- Keep the unpack sandboxed in a temp dir
When it happens
Trigger: Calling `shuttle::unpack` on an archive whose member header path is absolute (e.g. `/etc/passwd`) or contains a parent-directory component (e.g. `../../evil`). Detected by the malicious-entry tests.
Common situations: Installing a shuttle downloaded from an untrusted source; an archive produced by a buggy or tampered packer that stored raw absolute paths.
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
- capsule archive contains an unsafe path {}
- durable capsule archive contains unsafe path
- malicious shuttle detected: links are not allowed ('{}')
- malicious shuttle detected: unsupported entry type for '{}'
- symlink {} resolves outside the capsule source root ({}); re
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/d33546534f8dd486.
Report an issue: GitHub.