ppy/osu · error · InvalidOperationException

Filename "{filenames.original}" is not allowed.

Error message

Filename "{filenames.original}" is not allowed.

What it means

Thrown by RealmArchiveModelImporter during archive import when getShortenedFilenames(archive) yields a shortened filename that FilesystemSanityCheckHelpers.IncursPathTraversalRisk flags. The exception message echoes the ORIGINAL filename, not the shortened one. This guards the import path against malicious or malformed archive entries that try to escape the files directory.

Source

Thrown at osu.Game/Database/RealmArchiveModelImporter.cs:374

                    LogForModel(item, @"Found existing (optimised) but failed pre-check.");
                }
            }

            try
            {
                // Log output here will be missing a valid hash in non-batch imports.
                LogForModel(item, $@"Beginning import from {archive?.Name ?? "unknown"}...");

                List<RealmNamedFileUsage> files = new List<RealmNamedFileUsage>();

                if (archive != null)
                {
                    // Import files to the disk store.
                    // We intentionally delay adding to realm to avoid blocking on a write during disk operations.
                    foreach (var filenames in getShortenedFilenames(archive))
                    {
                        if (FilesystemSanityCheckHelpers.IncursPathTraversalRisk(filenames.shortened))
                            throw new InvalidOperationException($@"Filename ""{filenames.original}"" is not allowed.");

                        using (Stream s = archive.GetStream(filenames.original))
                            files.Add(new RealmNamedFileUsage(Files.Add(s, realm, false, parameters.PreferHardLinks), filenames.shortened));
                    }
                }

                using (var transaction = realm.BeginWrite())
                {
                    // Add all files to realm in one go.
                    // This is done ahead of the main transaction to ensure we can correctly cleanup the files, even if the import fails.
                    foreach (var file in files)
                    {
                        if (!file.File.IsManaged)
                            realm.Add(file.File, true);
                    }

                    transaction.Commit();
                }

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Reject or rewrite offending entry names before import: take Path.GetFileName(entry) and drop any directory traversal.
  2. Validate archives at the source; if from untrusted upload, run entries through IncursPathTraversalRisk and quarantine failures.
  3. If a legitimately-nested archive structure is intended, confirm getShortenedFilenames' shortening rules and that relative subpaths are preserved without traversal.

Example fix

// before
foreach (var filenames in getShortenedFilenames(archive))
{
    if (FilesystemSanityCheckHelpers.IncursPathTraversalRisk(filenames.shortened))
        throw new InvalidOperationException($"Filename \"{filenames.original}\" is not allowed.");
    // ...
}

// after (skip & report instead of aborting whole import)
foreach (var filenames in getShortenedFilenames(archive))
{
    if (FilesystemSanityCheckHelpers.IncursPathTraversalRisk(filenames.shortened))
    {
        LogForModel(item, $"Skipped unsafe entry: {filenames.original}");
        continue;
    }
    // ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-scan archive entries before import and quarantine unsafe ones.
foreach (var entry in archive filenames)
{
    if (FilesystemSanityCheckHelpers.IncursPathTraversalRisk(entry.shortened))
        unsafeEntries.Add(entry.original);
}
if (unsafeEntries.Any()) throw new InvalidDataException($"Unsafe entries: {string.Join(", ", unsafeEntries)}");

Type guard

static bool IsSafeArchiveEntry(string entry)
    => !string.IsNullOrWhiteSpace(entry)
       && !Path.IsPathRooted(entry)
       && !entry.Contains("..")
       && !FilesystemSanityCheckHelpers.IncursPathTraversalRisk(entry);

Try / catch

try { importer.Import(archive); }
catch (InvalidOperationException ex) when (ex.Message.Contains("is not allowed"))
{ /* report malicious/corrupt archive to user, abort import */ }

Prevention

When it happens

Trigger: Importing an archive (.osz/.osk/etc.) whose internal entry names contain '../', absolute paths, or other traversal patterns. The check runs on every entry produced by getShortenedFilenames before the entry's stream is opened and added to the store.

Common situations: Importing a user-supplied or downloaded archive with crafted entry names (Zip Slip); archives produced by tools that embed full paths or backslash separators; cross-platform archives where separators normalise into traversal.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/92c4e7ed413b033d. Report an issue: GitHub.