PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps
Продължаваме да правим използването на PVS-Studio по-удобно. Сега нашият анализатор е наличен в Chocolatey, мениджър за пакети за Windows. Считаме, че това ще улесни разгръщането на PVS-Studio, по-специално в облачни услуги. За да не се откланяме, ще проверим изходния код на същия Chocolatey. Като CI система ще използваме Azure DevOps.

Ето списък с други наши статии на тема интеграция с облачни системи:

Препоръчвам да обърнете внимание на първата статия за интеграция с Azure DevOps, тъй като в този случай някои моменти са пропуснати, за да не се дублират.

И така, героите на тази статия са:

PVS-Studio — инструмент за статичен анализ на код, предназначен за откриване на грешки и потенциални уязвимости в програми, написани на езици C, C++, C# и Java. Работи в 64-битови системи на Windows, Linux и macOS и може да анализира код, предназначен за 32-битови, 64-битови и вградени ARM платформи. Ако за първи път ще опитвате статичен анализ на код за проверка на своите проекти, препоръчваме да се запознаете с статия за това как бързо да видите най-интересните предупреждения на PVS-Studio и да оцените възможностите на този инструмент.

Azure DevOps — набор от облачни услуги, обхващащи целия процес на разработка. Платформата включва инструменти като Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, които позволяват да се ускори процесът на създаване на софтуер и да се повиши качеството му.

Chocolatey – мениджър за пакети за Windows с отворен код. Целта на проекта е да автоматизира целия жизнен цикъл на софтуера от инсталация до актуализация и деинсталация в операционните системи Windows.

За използването на Chocolatey

Можете да видите как да инсталирате самия мениджър за пакети по тази връзка. Пълната документация за инсталиране на анализатора е налична по връзка в раздела „Инсталиране с помощта на мениджъра за пакети Chocolatey“. Накратко ще повторя някои моменти оттам.

Командата за инсталиране на последната версия на анализатора:

choco install pvs-studio

Команда за инсталиране на конкретна версия на пакета PVS-Studio:

choco install pvs-studio --version=7.05.35617.2075

По подразбиране се инсталира само ядрото на анализатора — компонент Core. Всички останали флагове (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) могат да бъдат предадени чрез —package-parameters.

Примерна команда, която ще инсталира анализатора с плъгин за Visual Studio 2019:

choco install pvs-studio --package-parameters="'\/MSVS2019'"

Сега да разгледаме пример за удобно използване на анализатора под Azure DevOps.

Настройка

Напомням, че за такива моменти, като регистрация на акаунт, създаване на Build Pipeline и синхронизиране на акаунта с проект, намиращ се в репозиторий на GitHub, има отделна статия. Нашата настройка ще започне веднага с написване на конфигурационен файл.

Първо ще настроим триггер за стартиране, указвайки, че стартираме само за промени в master клон:

trigger:
- master

След това трябва да изберем виртуална машина. В момента това ще бъде Microsoft-hosted агент с Windows Server 2019 и Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Преминаваме към тялото на конфигурационния файл (блок steps). Въпреки че не можем да инсталираме произволен софтуер на виртуалната машина, не добавих контейнер Docker. Можем да добавим Chocolatey като разширение за Azure DevOps. За целта преминаваме по връзка. Натискаме Get it free. След това, ако вече сте аутентифицирани, просто изберете своя акаунт, а ако не, преминете през същите стъпки след аутентификация.

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Тук трябва да изберем къде ще добавим разширението и да натиснем бутона Инсталирайте.

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

След успешна инсталация натискаме Proceed to organization:

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Сега можем да видим шаблон за задача Chocolatey в прозореца tasks при редактиране на конфигурационния файл azure-pipelines.yml:

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Натискаме на Chocolatey и виждаме списък с полета:

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Тук трябва да изберем install в полето с команди. В Nuspec File Name ще посочим името на необходимия пакет – pvs-studio. Ако не посочим версия, ще се инсталира последната, което напълно ни устройва. Натискаме бутона add и виждаме формулирана задача в конфигурационния файл.

steps:
- task: ChocolateyCommand@0
  inputs:
    command: 'install'
    installPackageId: 'pvs-studio'

След това преминаваме към основната част на нашия файл:

- task: CmdLine@2
  inputs:
    script: 

Сега трябва да създадем файл с лицензия за анализатора. Тук PVSNAME и PVSKEY – са имената на променливите, стойностите на които указваме в настройките. Те ще съхраняват логин и лицензионен ключ PVS-Studio. За да зададем техните стойности, отваряме менюто Variables->New variable. Ще създадем променливи PVSNAME за логин и PVSKEY за ключа на анализатора. Не забравяйте да поставите отметка Дръжте тази стойност в тайна за PVSKEY. Команден код:

сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials 
–u $(PVSNAME) –n $(PVSKEY)

Нека да съберем проекта с помощта на bat-файла, който се намира в репозитория:

сall build.bat

Нека да създадем папка, в която да се съхраняват файловете с резултатите от анализа:

сall mkdir PVSTestResults

Нека стартираме анализа на проекта:

сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog 

Конвертирайте нашия отчет в HTML формат с помощта на утилитата PlogConverter:

сall "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
–t html –o PVSTestResults .PVSTestResultsChoco.plog

Сега е необходимо да създадем задача, за да можем да експортираме отчета.

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

Целият конфигурационен файл изглежда така:

trigger:
- master

pool:
  vmImage: 'windows-latest'

steps:
- task: ChocolateyCommand@0
  inputs:
    command: 'install'
    installPackageId: 'pvs-studio'

- task: CmdLine@2
  inputs:
    script: |
      call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
      credentials –u $(PVSNAME) –n $(PVSKEY)
      call build.bat
      call mkdir PVSTestResults
      call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
      –t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
      call "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
      –t html –o .PVSTestResults .PVSTestResultsChoco.plog

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

Натискаме Запази->Запази->Изпълни за стартиране на задачата. Ще експортираме отчета, като влезем в раздела за задачи.

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Проектът Chocolatey съдържа общо 37615 реда C# код. Нека да разгледаме някои от откритите грешки.

Резултати от проверката

Предупреждение N1

Предупреждение от анализатора: V3005 Променливата 'Provider' е присвоена на самата себе си. CrytpoHashProviderSpecs.cs 38

public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
  ....
  protected CryptoHashProvider Provider;
  ....
  public override void Context()
  {
    Provider = Provider = new CryptoHashProvider(FileSystem.Object);
  }
}

Анализаторът откри присвояване на променлива на сама себе си, което няма смисъл. Вероятно на мястото на една от тези променливи трябва да се намира някаква друга. Или е опечатка, и излишното присвояване може да бъде просто премахнато.

Предупреждение N2

Предупреждение от анализатора: V3093 [CWE-480] Операторът '&' оценява и двата операнда. Може би вместо това трябва да се използва операторът за кратко спиране '&&'. Platform.cs 64

public static PlatformType get_platform()
{
  switch (Environment.OSVersion.Platform)
  {
    case PlatformID.MacOSX:
    {
      ....
    }
    case PlatformID.Unix:
    if(file_system.directory_exists("/Applications")
      & file_system.directory_exists("/System")
      & file_system.directory_exists("/Users")
      & file_system.directory_exists("/Volumes"))
      {
        return PlatformType.Mac;
      }
        else
          return PlatformType.Linux;
    default:
      return PlatformType.Windows;
  }
}

Разликата на оператора & от оператора && се състои в това, че ако лявата страна на израза — false, все равно будет вычислена правая часть, что в данном случае подразумевает лишние вызовы метода system.directory_exists.

В рассмотренном фрагменте это мелкий недочёт. Да, данное условие можно оптимизировать, заменив оператор & на оператор &&, но с практической точки зрения это ни на что не влияет. Однако в других случаях путаница между & и && может вызывать серьезные проблемы, когда правая часть выражения будет работать с некорректными/недопустимыми значениями. Например, в нашей коллекции ошибок, выявленных с помощью диагностики V3093, есть вот такой случай:

if ((k < nct) & (s[k] != 0.0))

Даже если индекс k некорректен, он будет использоваться для доступа к элементу массива. В результате будет сгенерировано исключение IndexOutOfRangeException.

Предупреждения N3, N4

Предупреждение от анализатора: V3022 [CWE-571] Expression ‘shortPrompt’ is always true. InteractivePrompt.cs 101
Предупреждение от анализатора: V3022 [CWE-571] Expression ‘shortPrompt’ is always true. InteractivePrompt.cs 105

public static string 
prompt_for_confirmation(.... bool shortPrompt = false, ....)
{
  ....
  if (shortPrompt)
  {
    var choicePrompt = choice.is_equal_to(defaultChoice) //1
    ?
    shortPrompt //2
    ?
    "[[{0}]{1}]".format_with(choice.Substring(0, 1).ToUpperInvariant(), //3
    choice.Substring(1, choice.Length - 1))
    :
    "[{0}]".format_with(choice.ToUpperInvariant()) //0
    : 
    shortPrompt //4
    ? 
    "[{0}]{1}".format_with(choice.Substring(0, 1).ToUpperInvariant(), //5
    choice.Substring(1, choice.Length - 1)) 
    :
    choice; //0
    ....
  }
  ....
}

В данном случае имеет место быть странная логика работы тернарного оператора. Рассмотрим подробнее: если выполнится условие, помеченное мной цифрой 1, то мы перейдём к условию 2, которое всегда true, а значит, выполнится строчка 3. Если же условие 1 окажется ложным, то мы перейдём на строчку, помеченную цифрой 4, условие в которой тоже всегда true, а значит, выполнится строчка 5. Таким образом, условия, помеченные комментарием 0, никогда не будут выполнены, что может являться не совсем той логикой работы, на которую рассчитывал программист.

Предупреждение N5

Предупреждение от анализатора: V3123 [CWE-783] Perhaps the ‘?:’ operator works in a different way than it was expected. Its priority is lower than priority of other operators in its condition. Options.cs 1019

private static string GetArgumentName (...., string description)
{
  string[] nameStart;
  if (maxIndex == 1)
  {
    nameStart = new string[]{"{0:", "{"};
  }
  else
  {
    nameStart = new string[]{"{" + index + ":"};
  }
  for (int i = 0; i = 0 & j != 0 ? description [j++ - 1] == '{' : false);
    ....
    return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
  }
}

Диагностика сработала на строку:

while (start >= 0 & j != 0 ? description [j++ - 1] == '{' : false)

Так как переменная j по-горе инициализирана с нула, тернарният оператор ще върне стойност false. Поради това условие, тялото на цикъла ще се изпълни само веднъж. Мисля, че този фрагмент от кода не работи така, както е замислил програмистът.

Предупреждение N6

Предупреждение от анализатора: V3022 [CWE-571] Изразът ‘installedPackageVersions.Count != 1’ е винаги истинен. NugetService.cs 1405

private void remove_nuget_cache_for_package(....)
{
  if (!config.AllVersions && installedPackageVersions.Count > 1)
  {
    const string allVersionsChoice = "Всички версии";
    if (installedPackageVersions.Count != 1)
    {
      choices.Add(allVersionsChoice);
    }
    ....
  }
  ....
}

Тук има странно вложено условие: installedPackageVersions.Count != 1, което винаги ще бъде true. Често такова предупреждение показва логическа грешка в кода, а в останалите случаи просто излишна проверка.

Предупреждение N7

Предупреждение от анализатора: V3001 Има идентични подизрази ‘commandArguments.contains("-apikey")’ отляво и отдясно на оператора ‘||’. ArgumentsUtility.cs 42

public static bool arguments_contain_sensitive_information(string
 commandArguments)
{
  return commandArguments.contains("-install-arguments-sensitive")
  || commandArguments.contains("-package-parameters-sensitive")
  || commandArguments.contains("apikey ")
  || commandArguments.contains("config ")
  || commandArguments.contains("push ")
  || commandArguments.contains("-p ")
  || commandArguments.contains("-p=")
  || commandArguments.contains("-password")
  || commandArguments.contains("-cp ")
  || commandArguments.contains("-cp=")
  || commandArguments.contains("-certpassword")
  || commandArguments.contains("-k ")
  || commandArguments.contains("-k=")
  || commandArguments.contains("-key ")
  || commandArguments.contains("-key=")
  || commandArguments.contains("-apikey")
  || commandArguments.contains("-api-key")
  || commandArguments.contains("-apikey")
  || commandArguments.contains("-api-key");
}

Програмистът, който написа този участък код, е копирал две последни реда и е забравил да ги редактира. Поради това потребителите на Chocolatey са лишени от възможността да приложат параметър apikey по още два начина. Подобно на горните параметри, мога да предложа следните опции:

commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");

Грешките при копиране и поставяне имат голям шанс рано или късно да се появят в всеки проект с голямо количество изходен код, и едно от най-добрите средства за борба с тях – статичният анализ.

P.S. И както винаги, тази грешка има склонност да се появи в края на многострочно условие :). Вижте публикацията "Ефектът на последния ред".

Предупреждение N8

Предупреждение от анализатора: V3095 [CWE-476] Обектът ‘installedPackage’ е използван преди да бъде проверен срещу null. Проверете редове: 910, 917. NugetService.cs 910

публичен виртуален ConcurrentDictionary get_outdated(....)
{
  ....
  var pinnedPackageResult = outdatedPackages.GetOrAdd(
    packageName, 
    new PackageResult(installedPackage, 
                      _fileSystem.combine_paths(
                        ApplicationParameters.PackagesLocation, 
                        installedPackage.Id)));
  ....
  if (   installedPackage != null
      && !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion) 
      && !config.UpgradeCommand.ExcludePrerelease)
  {
    ....
  }
  ....
}

Класическа грешка: обектът се използва преди проверка installedPackage се използва, а след това се проверява за null. Тази диагностика показва, че имаме един от два проблема в програмата: или installedPackage никога не е равен null, което е съмнително, и проверката е излишна, или потенциално можем да получим сериозна грешка в кода – опит за достъп чрез нулева референция.

Заключение

Ето, направихме още една малка крачка – сега използването на PVS-Studio стана още по-лесно и удобно. Също така искам да кажа, че Chocolatey е добър пакетен мениджър с малко количество грешки в кода, които можеха да бъдат дори по-малко при използване на PVS-Studio.

Поканваме изтегли и да опитате PVS-Studio. Редовната употреба на статичен анализатор ще повиши качеството и надеждността на кода, разработван от вашия екип, и ще помогне за предотвратяване на много уязвимости на нулеви дни.

P.S.

Преди публикуването изпратихме статията на разработчиците на Сhocolatey и те я приеха добре. Ние не открихме нищо критично, но им например им хареса откритата от нас грешка, свързана с ключа „api-key“.

PVS-Studio вече в Chocolatey: проверка на Chocolatey от Azure DevOps

Ако искате да споделите тази статия с англоезичната аудитория, моля, използвайте връзката към превода: Владислав Столяров. PVS-Studio сега е в Chocolatey: проверка на Chocolatey под Azure DevOps.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster