{"record":{"id":"6fec75e79f52a263","repo":"microsoft/aspire","slug":"zip-entry-entry-fullname-would-extract-outside-the","errorCode":null,"errorMessage":"Zip entry '{entry.FullName}' would extract outside the destination directory.","messagePattern":"Zip entry '(.+?)' would extract outside the destination directory\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Utils/ArchiveHelper.cs","lineNumber":52,"sourceCode":"    }\n\n    private static void ExtractZipSafe(string archivePath, string destinationPath)\n    {\n        var normalizedDestination = Path.GetFullPath(destinationPath);\n\n        using var archive = ZipFile.OpenRead(archivePath);\n        foreach (var entry in archive.Entries)\n        {\n            if (string.IsNullOrEmpty(entry.FullName))\n            {\n                continue;\n            }\n\n            var fullPath = Path.GetFullPath(Path.Combine(destinationPath, entry.FullName));\n            if (!fullPath.StartsWith(normalizedDestination + Path.DirectorySeparatorChar, StringComparison.Ordinal) &&\n                !fullPath.Equals(normalizedDestination, StringComparison.Ordinal))\n            {\n                throw new InvalidOperationException($\"Zip entry '{entry.FullName}' would extract outside the destination directory.\");\n            }\n\n            if (entry.FullName.EndsWith('/') || entry.FullName.EndsWith('\\\\'))\n            {\n                Directory.CreateDirectory(fullPath);\n            }\n            else\n            {\n                var dir = Path.GetDirectoryName(fullPath);\n                if (dir is not null)\n                {\n                    Directory.CreateDirectory(dir);\n                }\n                entry.ExtractToFile(fullPath, overwrite: true);\n            }\n        }\n    }\n","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Utils/ArchiveHelper.cs#L34-L70","documentation":"ExtractZipSafe validates every zip entry's resolved full path against the normalized destination directory and throws InvalidOperationException if the entry would land outside it. This blocks zip-slip path traversal attacks via entries containing '..' segments or absolute paths.","triggerScenarios":"Extracting a zip whose entry FullName resolves outside the destination after Path.GetFullPath(Path.Combine(destination, entry.FullName)) — e.g. entry names like '../../evil.exe' or absolute paths.","commonSituations":"A malicious or corrupted archive from an untrusted source; a legitimately mis-packaged archive with '..' entry names; testing the security guard with crafted archives.","solutions":["Obtain the archive from a trusted source and re-download/verify it.","Inspect entry names (e.g. with a zip listing tool) and repack entries with relative, in-bounds paths.","If this is your own packaging pipeline, fix the tool that produced entries with absolute or '..' paths."],"exampleFix":"// before\n// entry: \"../../outside.dll\"\n// after\n// repack so all entries are relative and inside the archive root: \"lib/outside.dll\"","handlingStrategy":"try-catch","validationCode":"using var archive = ZipFile.OpenRead(archivePath);\nvar destFull = Path.GetFullPath(destinationPath);\nvar unsafeEntry = archive.Entries.FirstOrDefault(e =>\n    !Path.GetFullPath(Path.Combine(destFull, e.FullName)).StartsWith(destFull + Path.DirectorySeparatorChar, StringComparison.Ordinal));","typeGuard":null,"tryCatchPattern":"try { await ArchiveHelper.ExtractAsync(zipPath, dest, env, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"would extract outside\")) { // reject untrusted archive, log entry name\n}","preventionTips":["Only extract archives from trusted, verified sources.","Checksum-verify downloads before extraction.","Extract into a fresh, dedicated temp directory.","Never point extraction at a shared or root-level directory."],"tags":["cli","zip","security","path-traversal"],"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"}