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

Тук трябва да изберем инсталиране в полето с команди. В 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;
  }
}

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

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

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

Дори ако индексът k е некоректен, той ще се използва за достъп до елемента на масива. В резултат ще бъде генерирано изключение IndexOutOfRangeException.

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

Предупреждение на анализатора: V3022 [CWE-571] Изразът ‘shortPrompt’ винаги е верен. InteractivePrompt.cs 101
Предупреждение на анализатора: V3022 [CWE-571] Изразът ‘shortPrompt’ винаги е верен. 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, което винаги истинно, следователно ще се изпълни ред 3. Ако условие 1 се окаже невярно, ще преминем на реда, маркиран с цифра 4, условието в което също винаги истинно, следователно ще се изпълни ред 5. Така условията, маркирани с коментар 0, никога няма да бъдат изпълнени, което може да не е точно логиката на работа, на която се е надявал програмистът.

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

Предупреждение на анализатора: V3123 [CWE-783] Може би ‘?:’ операторът работи по различен начин от очаквания. Неговият приоритет е по-нисък от приоритета на другите оператори в условието му. 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 е инициализирана с нула няколко реда по-горе, тернарният оператор ще върне стойност неверно. Поради това условие, тялото на цикъла ще се изпълни само веднъж. Смятам, че този фрагмент от кода не работи по начина, по който разработчикът е замислил.

Предупреждение 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, което винаги ще бъде истинно. Често такова предупреждение показва логическа грешка в кода, а в останалите случаи просто излишна проверка.

Предупреждение 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<низ, PackageResult> get_outdated(....)
{
  ....
  var pinnedPackageResult = outdatedPackages.GetOrAdd(
    packageName, 
    нов 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.

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

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

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

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

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