SubtitleEdit/subtitleedit · error · InvalidDataException
Archive entry is rooted outside the extraction folder: {read
Error message
Archive entry is rooted outside the extraction folder: {reader.Entry.Key} What it means
InvalidDataException raised by a Zip-Slip / path-traversal defense: after normalizing separators, the archive entry path is Path.IsPathRooted (an absolute path like '/etc/...' or 'C:\...'). The extractor refuses to write outside the target directory.
Source
Thrown at src/ui/Logic/SevenZipExtractor/Unpacker.cs:410
{
var entryFullName = reader.Entry.Key.Replace('\\', '/');
var normalizedSkipFolder = skipFolderLevel?.Replace('\\', '/').Trim('/') ?? string.Empty;
if (!string.IsNullOrEmpty(normalizedSkipFolder))
{
if (entryFullName.Equals(normalizedSkipFolder, StringComparison.Ordinal))
{
entryFullName = string.Empty;
}
else if (entryFullName.StartsWith(normalizedSkipFolder + "/", StringComparison.Ordinal))
{
entryFullName = entryFullName[(normalizedSkipFolder.Length + 1)..];
}
}
entryFullName = entryFullName.Replace('/', Path.DirectorySeparatorChar);
if (Path.IsPathRooted(entryFullName))
{
throw new InvalidDataException($"Archive entry is rooted outside the extraction folder: {reader.Entry.Key}");
}
entryFullName = entryFullName.TrimStart(Path.DirectorySeparatorChar);
if (string.IsNullOrEmpty(entryFullName))
{
if (reader.Entry.IsDirectory)
{
Directory.CreateDirectory(dir);
continue;
}
throw new InvalidDataException("Archive contains an empty file entry name.");
}
var fullFileName = Path.GetFullPath(Path.Combine(targetRoot, entryFullName));
var relativePath = Path.GetRelativePath(targetRoot, fullFileName);
if (relativePath.Equals("..", StringComparison.Ordinal) ||
relativePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) ||View on GitHub (pinned to 17a9f07487)
Solutions
- Treat the archive as untrusted and reject it; do not bypass this guard.
- If you control the archive, rebuild it so all entries use relative paths.
- Verify the publisher/hash of the archive before extraction.
Example fix
// before
Unpacker.ExtractArchive(archivePath, dest);
// after
if (!IsTrustedPublisher(archivePath)) { logger.LogWarning("Rejecting archive with rooted entries"); return; }
try { Unpacker.ExtractArchive(archivePath, dest); }
catch (InvalidDataException ex) when (ex.Message.Contains("rooted outside")) { Quarantine(archivePath); } Defensive patterns
Strategy: validation
Validate before calling
foreach (var entry in ListEntries(archive))
{
var name = entry.Key.Replace('/', Path.DirectorySeparatorChar);
if (Path.IsPathRooted(name)) { Quarantine(archive); throw new InvalidDataException($"Refusing rooted entry {entry.Key}"); }
} Try / catch
try { Unpacker.ExtractArchive(archive, dest); }
catch (InvalidDataException ex) when (ex.Message.Contains("rooted outside"))
{
Quarantine(archive);
logger.LogWarning("Rejected rooted-entry archive {Archive}", archive);
} Prevention
- Only extract archives from trusted, verified publishers.
- Do not bypass the path-traversal guard.
- Scan entries for absolute paths before extraction.
- Verify the archive hash against the publisher.
When it happens
Trigger: An archive entry whose Key, after the skip-folder prefix is stripped and '/' is replaced with the OS separator, resolves to an absolute path. Typical of a maliciously crafted or malformed archive.
Common situations: Third-party or untrusted plugin/update archives, or archives produced on another OS that embed drive letters or leading slashes.
Related errors
- Archive entry is outside the extraction folder: {reader.Entr
- Archive contains an empty file entry name.
- Too many bytes for CCData!
- Color '{colorName}' not found in SKColors.
- End of file reached before expected
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/dbacf640507fc375.
Report an issue: GitHub.