{"record":{"id":"cdc16cb30a8db087","repo":"microsoft/aspire","slug":"tar-entry-entry-name-would-extract-outside-the-destination","errorCode":null,"errorMessage":"Tar entry '{entry.Name}' would extract outside the destination directory.","messagePattern":"Tar entry '(.+?)' would extract outside the destination directory\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Bundles/BundleService.cs","lineNumber":898,"sourceCode":"            if (slashIndex < 0)\n            {\n                continue; // Top-level directory entry itself, skip\n            }\n\n            var relativePath = name[(slashIndex + 1)..];\n            if (string.IsNullOrEmpty(relativePath))\n            {\n                continue;\n            }\n\n            var fullPath = Path.GetFullPath(Path.Combine(destinationPath, relativePath));\n            var normalizedDestination = Path.GetFullPath(destinationPath);\n\n            // Guard against path traversal attacks (e.g., entries containing \"..\" segments)\n            if (!fullPath.StartsWith(normalizedDestination + Path.DirectorySeparatorChar, StringComparison.Ordinal) &&\n                !fullPath.Equals(normalizedDestination, StringComparison.Ordinal))\n            {\n                throw new InvalidOperationException($\"Tar entry '{entry.Name}' would extract outside the destination directory.\");\n            }\n\n            switch (entry.EntryType)\n            {\n                case TarEntryType.Directory:\n                    Directory.CreateDirectory(fullPath);\n                    break;\n\n                case TarEntryType.RegularFile:\n                    var dir = Path.GetDirectoryName(fullPath);\n                    if (dir is not null)\n                    {\n                        Directory.CreateDirectory(dir);\n                    }\n                    await entry.ExtractToFileAsync(fullPath, overwrite: true, cancellationToken);\n\n                    // Preserve Unix file permissions from tar entry (e.g., execute bit)\n                    if (!environment.IsWindows() && entry.Mode != default)","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Bundles/BundleService.cs#L880-L916","documentation":"During bundle payload extraction, BundleService validates every tar entry's resolved full path against the normalized destination directory to block path traversal ('..' or absolute-name entries). If an entry would extract outside the destination, it throws InvalidOperationException naming the offending entry. This is a security guard against malicious or malformed bundle archives.","triggerScenarios":"Extracting a tar.gz whose entry name contains '..' segments, absolute paths, or symlink targets that resolve outside the destination directory (line ~898 check).","commonSituations":"A corrupted or hand-crafted/tampered bundle payload; a bundle built with absolute or ../-relative entry names by a faulty packaging script; MITM-substituted payload if the bundle was downloaded insecurely.","solutions":["Reinstall the Aspire CLI from the official source to get a known-good bundle payload.","Rebuild the bundle with a packaging script that emits relative, containerized entry names (no leading '/', no '..').","Verify the bundle's checksum against the published value to detect tampering or corruption.","If you control extraction, keep the existing guard and reject the archive rather than bypassing it."],"exampleFix":"// before (bad packaging script)\ntar -czf bundle.tar.gz /abs/path/files  // entries carry absolute names\n// after\ntar -czf bundle.tar.gz -C payload .     // entries are relative to the payload root","handlingStrategy":"validation","validationCode":"// pre-validate archive entry names before extraction\nforeach (var entry in archiveEntries)\n{\n    if (Path.IsPathRooted(entry.Name) || entry.Name.Split('/', '\\\\').Contains(\"..\"))\n        throw new InvalidOperationException($\"Unsafe tar entry: {entry.Name}\");\n}","typeGuard":"static bool IsSafeEntryName(string name) =>\n    !string.IsNullOrEmpty(name) && !Path.IsPathRooted(name) && !name.Split('/', '\\\\').Contains(\"..\");","tryCatchPattern":"try { await ExtractPayloadAsync(dest, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"would extract outside\")) { /* reject/tamper-check the bundle; reinstall from a trusted source */ }","preventionTips":["Only extract bundles obtained from official, checksum-verified sources","Build bundles with relative entry names (tar -C payload .)","Never bypass or weaken the destination-containment check"],"tags":["cli","security","path-traversal","extraction"],"backgroundTag":"path-traversal-blocked","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}