CA5389: не добавляйте путь к элементу архива в путь целевой файловой системы

Свойство Значение
Идентификатор правила CA5389
Заголовок Не добавлять путь к элементу архива в путь целевой файловой системы
Категория Безопасность
Исправление является критическим или не критическим Не критическое
Включен по умолчанию в .NET 8 No

Причина

В качестве пути к целевому файлу в одном из следующих параметров используется неочищенный путь к исходному файлу:

По умолчанию это правило анализирует всю базу кода, но такое поведение можно настроить.

Описание правила

Путь к файлу может быть относительным и привести к доступу к файловой системе за пределами ожидаемого целевого пути, повышая риск вредоносного изменения конфигурации и удаленного выполнения кода с помощью метода выжидания.

Устранение нарушений

Не используйте путь к исходному файлу для создания пути к целевому файлу или убедитесь, что последний символ в пути извлечения является символом разделителя каталога.

Когда лучше отключить предупреждения

Это предупреждение можно отключить, если исходный путь всегда поступает из доверенного источника.

Отключение предупреждений

Если вы просто хотите отключить одно нарушение, добавьте директивы препроцессора в исходный файл, чтобы отключить и повторно включить правило.

#pragma warning disable CA5389
// The code that's violating the rule is on this line.
#pragma warning restore CA5389

Чтобы отключить правило для файла, папки или проекта, задайте его серьезность none в файле конфигурации.

[*.{cs,vb}]
dotnet_diagnostic.CA5389.severity = none

Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.

Настройка кода для анализа

Используйте следующие параметры, чтобы указать части базы кода, к которым будет применяться это правило.

Эти параметры можно настроить только для этого правила, для всех правил, к которым она применяется, или для всех правил в этой категории (безопасности), к которым она применяется. Дополнительные сведения см. в статье Параметры конфигурации правила качества кода.

Исключение определенных символов

Вы можете исключить из анализа определенные символы, например типы и методы. Например, чтобы указать, что правило не должно выполняться для какого-либо кода в типах с именем MyType, добавьте следующую пару "ключ-значение" в файл EDITORCONFIG в своем проекте:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Допустимые форматы имени символа в значении параметра (разделенные |):

  • Только имя символа (включает все символы с этим именем, любого типа и в любом пространстве имен).
  • Полные имена в формате идентификатора документации для символа. Для каждого имени символа требуется префикс в виде символа, например M: для методов, T: для типов и N: для пространств имен.
  • .ctor используется для конструкторов, а .cctor — для статических конструкторов.

Примеры:

Значение параметра Итоги
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Соответствует всем символам с именем MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Соответствует всем символам с именем MyType1 или MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Соответствует конкретному методу MyMethod с заданной полной сигнатурой.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Соответствует конкретным методам MyMethod1 и MyMethod2 с соответствующими полными сигнатурами.

Исключить определенные типы и их производные типы

Из анализа можно исключать определенные типы и их производные типы. Например, чтобы указать, что правило не должно выполняться в каких-либо методах типов MyType и их производных типов, добавьте следующую пару "ключ-значение" в файл .editorconfig своего проекта:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Допустимые форматы имени символа в значении параметра (разделенные |):

Примеры:

Значение параметра Итоги
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Соответствует всем типам с именем MyType и всем их производным типам.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Соответствует всем типам с именем MyType1 или MyType2 и всем их производным типам.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Соответствует конкретному типу MyType с заданным полным именем и всем производным от него типам.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Соответствует конкретным типам MyType1 и MyType2 с заданным полным именем и всем производным от них типам.

Пример

В приведенном ниже фрагменте кода показан шаблон, обнаруживаемый этим правилом.

Нарушение:

using System.IO.Compression;

class TestClass
{
    public void TestMethod(ZipArchiveEntry zipArchiveEntry)
    {
        zipArchiveEntry.ExtractToFile(zipArchiveEntry.FullName);
    }
}

Решение.

using System;
using System.IO;
using System.IO.Compression;

class Program
{
    static void Main(string[] args)
    {
        string zipPath = @".\result.zip";

        Console.WriteLine("Provide path where to extract the zip file:");
        string extractPath = Console.ReadLine();

        // Normalizes the path.
        extractPath = Path.GetFullPath(extractPath);

        // Ensures that the last character on the extraction path
        // is the directory separator char.
        // Without this, a malicious zip file could try to traverse outside of the expected
        // extraction path.
        if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
            extractPath += Path.DirectorySeparatorChar;

        using (ZipArchive archive = ZipFile.OpenRead(zipPath))
        {
            foreach (ZipArchiveEntry entry in archive.Entries)
            {
                if (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
                {
                    // Gets the full path to ensure that relative segments are removed.
                    string destinationPath = Path.GetFullPath(Path.Combine(extractPath, entry.FullName));

                    // Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
                    // are case-insensitive.
                    if (destinationPath.StartsWith(extractPath, StringComparison.Ordinal))
                        entry.ExtractToFile(destinationPath);
                }
            }
        }
    }
}