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

  1. Treat the archive as untrusted and reject it; do not bypass this guard.
  2. If you control the archive, rebuild it so all entries use relative paths.
  3. 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

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


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/dbacf640507fc375. Report an issue: GitHub.