{"record":{"id":"c0662a46180a7ac3","repo":"rust-lang/cargo","slug":"invalid-tarball-downloaded-contains-a-file-at-en","errorCode":null,"errorMessage":"invalid tarball downloaded, contains a file at {entry_path:?} which isn't under {prefix:?}","messagePattern":"invalid tarball downloaded, contains a file at (.+?) which isn't under (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/sources/registry/mod.rs","lineNumber":999,"sourceCode":"    for entry in tar.entries()? {\n        let mut entry = entry.context(\"failed to iterate over archive\")?;\n        let entry_path = entry\n            .path()\n            .context(\"failed to read entry path\")?\n            .into_owned();\n\n        if let Ok(path) = entry_path.strip_prefix(prefix) {\n            if !include(path) {\n                continue;\n            }\n        } else {\n            // We're going to unpack this tarball into the global source\n            // directory, but we want to make sure that it doesn't accidentally\n            // (or maliciously) overwrite source code from other crates. Cargo\n            // itself should never generate a tarball that hits this error, and\n            // crates.io should also block uploads with these sorts of tarballs,\n            // but be extra sure by adding a check here as well.\n            anyhow::bail!(\n                \"invalid tarball downloaded, contains \\\n                     a file at {entry_path:?} which isn't under {prefix:?}\",\n            )\n        }\n\n        // Prevent unpacking symlinks and other unexpected entry types\n        match entry.header().entry_type() {\n            EntryType::Regular | EntryType::Directory => {}\n            t => anyhow::bail!(\n                \"invalid tarball downloaded, contains an entry at {entry_path:?} with invalid type {t:?}\",\n            ),\n        }\n\n        // Prevent unpacking the lockfile from the crate itself.\n        if entry_path\n            .file_name()\n            .map_or(false, |p| p == PACKAGE_SOURCE_LOCK)\n        {","sourceCodeStart":981,"sourceCodeEnd":1017,"githubUrl":"https://github.com/rust-lang/cargo/blob/98a09e7e7d62850f14e5b6132101fc1edd19a16f/src/sources/registry/mod.rs#L981-L1017","documentation":"While unpacking a `.crate` tarball, Cargo requires every entry's path to be under the package prefix (`<pkg>-<ver>/`). If `entry_path.strip_prefix(prefix)` fails, the tarball contains a file outside the expected root — a path-traversal or malformed-tarball safety violation. Cargo blocks it to prevent overwriting other crates' source.","triggerScenarios":"`unpack()` iterates tar entries; an entry path does not start with the package prefix. Caused by a tarball built with absolute paths, `..` segments, or missing the `<pkg>-<ver>/` root — i.e. a non-conformant or malicious `.crate` file.","commonSituations":"A hand-built / third-party-packaged `.crate` that omits the conventional root directory; a tampered or corrupted tarball; an old or non-standard tool producing flat tarballs; a registry mirror that re-packaged crates incorrectly.","solutions":["Re-download the official `.crate` from crates.io or the original registry to replace a corrupt/repackaged one.","If you publish crates, ensure packaging uses `cargo package` which emits the correct root prefix.","Clear cache: `rm ~/.cargo/registry/cache/<index>/<pkg>-<ver>.crate` and refetch.","Audit the registry mirror — reject re-packaged tarballs lacking the `<pkg>-<ver>/` prefix."],"exampleFix":"# before: tarball with flat (non-prefixed) layout\n$ tar tf serde-1.0.0.crate\nCargo.toml\nsrc/lib.rs\n\n# after: properly packaged (re-fetch from trusted registry)\n$ tar tf serde-1.0.0.crate\nserde-1.0.0/Cargo.toml\nserde-1.0.0/src/lib.rs","handlingStrategy":"validation","validationCode":"fn validate_tarball_prefix(tar: &tar::Archive<std::fs::File>, expected_prefix: &str) -> Result<(), anyhow::Error> {\n    for entry in tar.entries()? {\n        let e = entry?;\n        let p = e.path()?;\n        if !p.starts_with(expected_prefix) {\n            anyhow::bail!(\"entry outside prefix: {:?}\", p);\n        }\n    }\n    Ok(())\n}","typeGuard":"fn tarball_all_under_prefix(path: &std::path::Path, prefix: &str) -> bool {\n    let f = std::fs::File::open(path).ok();\n    f.and_then(|f| tar::Archive::new(f).entries().ok()).map_or(false, |mut es| {\n        es.by_ref().all(|e| e.map_or(false, |e| e.path().map_or(false, |p| p.starts_with(prefix))))\n    })\n}","tryCatchPattern":"if entry_path.strip_prefix(prefix).is_err() {\n    // do not bail hard if you can re-fetch: replace the crate and retry\n    refetch_crate(pkg)?;\n    return unpack(...);\n}","preventionTips":["Only consume `.crate` files from trusted registries.","Package crates with `cargo package` (correct root prefix).","Reject re-packaged tarballs in private registry mirrors.","Audit uploaded tarballs for path-traversal entries before publishing."],"tags":["cargo","registry","tarball","unpack","security","path-traversal","integrity"],"backgroundTag":null,"analyzedSha":"98a09e7e7d62850f14e5b6132101fc1edd19a16f","analyzedAt":"2026-08-11T17:42:36.556Z","contentChangedAt":"2026-08-11T17:42:36.556Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}