{"record":{"id":"939ae5fcd2be216f","repo":"SubtitleEdit/subtitleedit","slug":"archive-entry-is-outside-the-extraction-folder-r","errorCode":null,"errorMessage":"Archive entry is outside the extraction folder: {reader.Entry.Key}","messagePattern":"Archive entry is outside the extraction folder: (.+?)","errorType":"validation","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/ui/Logic/SevenZipExtractor/Unpacker.cs","lineNumber":431,"sourceCode":"                entryFullName = entryFullName.TrimStart(Path.DirectorySeparatorChar);\n                if (string.IsNullOrEmpty(entryFullName))\n                {\n                    if (reader.Entry.IsDirectory)\n                    {\n                        Directory.CreateDirectory(dir);\n                        continue;\n                    }\n\n                    throw new InvalidDataException(\"Archive contains an empty file entry name.\");\n                }\n\n                var fullFileName = Path.GetFullPath(Path.Combine(targetRoot, entryFullName));\n                var relativePath = Path.GetRelativePath(targetRoot, fullFileName);\n                if (relativePath.Equals(\"..\", StringComparison.Ordinal) ||\n                    relativePath.StartsWith(\"..\" + Path.DirectorySeparatorChar, StringComparison.Ordinal) ||\n                    Path.IsPathRooted(relativePath))\n                {\n                    throw new InvalidDataException($\"Archive entry is outside the extraction folder: {reader.Entry.Key}\");\n                }\n\n                if (reader.Entry.IsDirectory)\n                {\n                    if (!Directory.Exists(fullFileName))\n                    {\n                        Directory.CreateDirectory(fullFileName);\n                    }\n\n                    continue;\n                }\n\n                var fullPath = Path.GetDirectoryName(fullFileName);\n                if (fullPath == null)\n                {\n                    continue;\n                }\n","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Logic/SevenZipExtractor/Unpacker.cs#L413-L449","documentation":"InvalidDataException from the second Zip-Slip guard: after GetFullPath, the entry resolves to a path whose relative form equals '..' or starts with '..' (+separator) or is rooted, meaning it escapes targetRoot.","triggerScenarios":"Archive entries like '../evil.dll', '..\\\\..\\\\system32\\\\x', or nested traversal sequences that resolve above targetRoot despite not being absolute.","commonSituations":"Malicious archives designed to escape the extraction folder, or archives built from a parent directory.","solutions":["Treat the archive as untrusted and reject/quarantine it; this guard is load-bearing for security.","Rebuild the archive with safe, in-tree relative paths.","Verify publisher and hash before extraction."],"exampleFix":"// before\nUnpacker.ExtractArchive(archivePath, dest);\n\n// after\ntry { Unpacker.ExtractArchive(archivePath, dest); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"outside the extraction folder\"))\n{\n    Quarantine(archivePath);\n    logger.LogWarning(\"Rejected traversal attempt in {Archive}\", archivePath);\n}","handlingStrategy":"validation","validationCode":"foreach (var entry in ListEntries(archive))\n{\n    var full = Path.GetFullPath(Path.Combine(dest, entry.Key));\n    var rel = Path.GetRelativePath(dest, full);\n    if (rel.Equals(\"..\") || rel.StartsWith(\"..\" + Path.DirectorySeparatorChar) || Path.IsPathRooted(rel))\n    {\n        Quarantine(archive);\n        throw new InvalidDataException($\"Refusing traversal entry {entry.Key}\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try { Unpacker.ExtractArchive(archive, dest); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"outside the extraction folder\"))\n{\n    Quarantine(archive);\n    logger.LogWarning(\"Rejected traversal archive {Archive}\", archive);\n}","preventionTips":["Only extract trusted, publisher-verified archives.","Never suppress the traversal guard.","Pre-scan entries for '..' escape sequences."],"tags":["security","zip-slip","path-traversal","archive","validation"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}