
Ние продължаваме да правим използването на PVS-Studio по-удобно. Сега нашият анализатор е наличен в Chocolatey, мениджър за пакети за Windows. Смятаме, че това ще улесни внедряването на PVS-Studio, особено в облачни услуги. За да не ходим далеч, нека проверим изходния код на същия Chocolatey. Като CI система ще използваме Azure DevOps.
Ето списък с други наши статии на тема интеграция с облачни системи:
Препоръчвам да обърнете внимание на първата статия за интеграция с Azure DevOps, тъй като в този случай някои моменти са пропуснати, за да не се дублират.
И така, героите на тази статия:
— инструмент за статичен анализ на код, предназначен за откриване на грешки и потенциални уязвимости в програми, написани на езици C, C++, C# и Java. Работи в 64-битови системи на Windows, Linux и macOS и може да анализира код, предназначен за 32-битови, 64-битови и вградени ARM платформи. Ако ще опитвате статичен анализ на кода за проверка на вашите проекти за първи път, препоръчваме да се запознаете с како бързо да видите най-интерсните предупреждения на PVS-Studio и да оцените възможностите на този инструмент.
— набор от облачни услуги, обхващащи целия процес на разработка. В състава на тази платформа влизат инструменти като Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, които позволяват да се ускори процесът на създаване на софтуер и да се повиши неговото качество.
– мениджър за пакети за 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. След това, ако вече сте автентифицирани, просто изберете своя акаунт, а ако не, направете същото след автентикация.

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

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

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

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

Тук трябва да изберем инсталиране в полето с команди. В 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()Натиснете Запази->Запази->Стартиране за стартиране на задачата. Експортирайте отчета, като влезете в таба за задачи.
Проектът Chocolatey съдържа общо 37615 реда C# код. Нека разгледаме някои от намерените грешки.
Резултати от анализа
Предупреждение N1
Предупреждение на анализатора: Променливата ‘Provider’ е присвоена сама на себе си. CrytpoHashProviderSpecs.cs 38
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}Анализаторът е открил присвояване на променлива сама на себе си, което няма смисъл. Вероятно на мястото на една от тези променливи трябва да стои друга. Или може би е правописна грешка и излишното присвояване може просто да се премахне.
Предупреждение N2
Предупреждение на анализатора: [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.
В разглеждания фрагмент това е незначителен пропуск. Да, това условие може да се оптимизира, заменяйки оператора & на оператора &&, но от практическа гледна точка, това не оказва влияние. Въпреки това, в други случаи объркването между & и && може да предизвика сериозни проблеми, когато дясната част на израза работи с некоректни/недопустими стойности. Например, в нашата колекция от грешки, , имаме следния случай:
if ((k < nct) & (s[k] != 0.0))Дори ако индексът k е некоректен, той ще се използва за достъп до елемента на масива. В резултат ще бъде генерирано изключение IndexOutOfRangeException.
Предупреждения N3, N4
Предупреждение на анализатора: [CWE-571] Изразът ‘shortPrompt’ винаги е верен. InteractivePrompt.cs 101
Предупреждение на анализатора: [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
Предупреждение на анализатора: [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
Предупреждение на анализатора: [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
Предупреждение на анализатора: Има идентични подизрази ‘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
Предупреждение на анализатора: [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.“
Ако искате да споделите тази статия с англоговорящата аудитория, моля, използвайте връзката към превода: Владислав Столяров. .
Източник: habr.com
