
Pokračujeme ve zjednodušení používání PVS-Studio. Nyní je náš analyzátor dostupný v Chocolatey, správci balíčků pro Windows. Věříme, že to usnadní nasazení PVS-Studio, zejména v cloudových službách. Abychom nešli daleko, podívejme se na zdrojový kód samotného Chocolatey. CI systémem bude Azure DevOps.
Zde je seznam našich dalších článků na téma integrace s cloudovými systémy:
Doporučuji zaměřit se na první článek o integraci s Azure DevOps, protože zde byly některé body vynechány, aby nedošlo k duplicování.
A tak, hrdinové tohoto článku:
— nástroj pro statickou analýzu kódu, určený k odhalování chyb a potenciálních zranitelností v programech napsaných v jazycích C, C++, C# a Java. Funguje na 64bitových systémech Windows, Linux a macOS a může analyzovat kód určený pro 32bitové, 64bitové a vestavěné ARM platformy. Pokud se statickou analýzou kódu pro kontrolu svých projektů setkáváte poprvé, doporučujeme se seznámit se jak rychle zobrazit nejzajímavější upozornění PVS-Studio a ocenit možnosti tohoto nástroje.
— sada cloudových služeb, které dohromady pokrývají celý proces vývoje. Tato platforma zahrnuje nástroje jako Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, které pomáhají zrychlit proces vytváření softwaru a zvýšit jeho kvalitu.
– správce balíčků pro Windows s otevřeným zdrojovým kódem. Cílem projektu je automatizovat celý životní cyklus softwaru od instalace po aktualizaci a odstraňování v operačních systémech Windows.
O používání Chocolatey
Podívat se, jak nainstalovat samotného správce balíčků, můžete na této . Komplexní dokumentace k instalaci analyzátoru je k dispozici na v sekci „Instalace pomocí správce balíčků Chocolatey“. Stručně opakuji některé body odtud.
Příkaz pro instalaci nejnovější verze analyzátoru:
choco install pvs-studioPříkaz pro instalaci konkrétní verze balíčku PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Výchozí instalací se nastaví pouze jádro analyzátoru — komponent Core. Všechny ostatní příznaky (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) lze předat pomocí —package-parameters.
Příklad příkazu, který nainstaluje analyzátor s pluginem pro Visual Studio 2019:
choco install pvs-studio --package-parameters="'/MSVS2019'"Nyní se podíváme na příklad pohodlného použití analyzátoru v Azure DevOps.
Nastavení
Připomínám, že problémy jako registrace účtu, vytvoření Build Pipeline a synchronizace účtu s projektem v repozitáři na GitHubu, mají svůj vlastní . Naše nastavení začne přímo psaním konfiguračního souboru.
Nejprve nastavíme spouštěcí trigger, a to tak, že uvedeme, že provádíme spuštění pouze pro změny v master větvi:
trigger:
- masterDále musíme vybrat virtuální stroj. V tuto chvíli to bude Microsoft-hosted agent s Windows Server 2019 a Visual Studio 2019:
pool:
vmImage: 'windows-latest'Přejdeme k tělu konfiguračního souboru (blok steps). I když není možné na virtuální stroj nainstalovat libovolný software, nepřidával jsem Docker kontejner. Můžeme přidat Chocolatey jako rozšíření pro Azure DevOps. K tomu přejdeme na . Klikneme na Získat zdarma. Dále, pokud jste již přihlášeni, jednoduše vyberte svůj účet, a pokud ne, proveďte to po přihlášení.

Zde je třeba vybrat, kam přidáme rozšíření, a stisknout tlačítko Nainstalovat.

Po úspěšné instalaci klikneme na Pokračovat k organizaci:

Nyní můžeme vidět šablonu úlohy Chocolatey v okně tasks při úpravě konfiguračního souboru azure-pipelines.yml:

Klikneme na Chocolatey a uvidíme seznam polí:

Zde musíme vybrat install v poli s příkazy. V Nuspec File Name uvedeme název požadovaného balíčku – pvs-studio. Pokud neuvedeme verzi, nainstaluje se poslední, což nám zcela vyhovuje. Klikneme na tlačítko add a uvidíme vytvořenou úlohu v konfiguračním souboru.
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'Dále přejdeme k hlavní části našeho souboru:
- task: CmdLine@2
inputs:
script: Nyní musíme vytvořit soubor s licencí analyzátoru. Zde PVSNAME a PVSKEY – názvy proměnných, jejichž hodnoty uvádíme v nastavení. Budou obsahovat přihlašovací jméno a licenční klíč PVS-Studio. Abychom nastavili jejich hodnoty, otevřeme nabídku Proměnné->Nová proměnná. Vytvoříme proměnné PVSNAME pro přihlašovací jméno a PVSKEY pro klíč analyzátoru. Nezapomeňte zaškrtnout Tuto hodnotu si uchovejte v tajnosti pro PVSKEY. Příkazový kód:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)Sestavíme projekt pomocí bat-souboru uloženého v repozitáři:
call build.batVytvoříme složku, kde budou uloženy soubory s výsledky práce analyzátoru:
call mkdir PVSTestResultsSpustíme analýzu projektu:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog Převedeme naši zprávu do formátu html pomocí nástroje PlogConverter:
call "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogNyní je potřeba vytvořit úlohu pro export zprávy.
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()Celý konfigurační soubor vypadá takto:
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()Klikněte Uložit->Uložit->Spustit pro spuštění úlohy. Exportujeme zprávu v sekci úlohy.
Projekt Chocolatey obsahuje pouze 37615 řádků kódu C#. Podívejme se na některé z nalezených chyb.
Výsledky kontroly
Upozornění N1
Varování analyzátoru: Proměnná 'Provider' je přiřazena sama sobě. CrytpoHashProviderSpecs.cs 38
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}Analyzátor zjistil přiřazení proměnné k sobě samé, což nedává smysl. Pravděpodobně by na místě jedné z těchto proměnných měla být nějaká jiná. Nebo je to chyba, a zbytečné přiřazení může být jednoduše odstraněno.
Upozornění N2
Varování analyzátoru: [CWE-480] Operátor '&&' vyhodnocuje obě operand. Mělo by se pravděpodobně místo toho použít zkrácený operátor '&&'. 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;
}
}Rozdíl mezi operátorem && a operátorem && je v tom, že pokud je levá strana výrazu - false, přesto bude spočítána pravá strana, což v tomto případě znamená zbytečné volání metody system.directory_exists.
Ve zvažovaném fragmentu se jedná o drobnou chybu. Ano, tuto podmínku lze optimalizovat, pokud nahradíme operator & operator &&, ale z praktického hlediska to na nic nemá vliv. Nicméně, v jiných případech může zmatení mezi & a && způsobit vážné problémy, pokud bude pravá strana výrazu pracovat s neplatnými/zakázanými hodnotami. Například v naší sbírce chyb, , máme takový případ:
if ((k < nct) & (s[k] != 0.0))I když je index k neplatný, bude použit k přístupu k prvku pole. V důsledku toho dojde k vyvolání výjimky IndexOutOfRangeException.
Varování N3, N4
Varování analyzátoru: [CWE-571] Výraz ‘shortPrompt’ je vždy pravdivý. InteractivePrompt.cs 101
Varování analyzátoru: [CWE-571] Výraz ‘shortPrompt’ je vždy pravdivý. 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
....
}
....
}V tomto případě je přítomna podivná logika fungování ternárního operátoru. Podívejme se blíže: pokud bude splněna podmínka označená mnou číslem 1, přejdeme k podmínce 2, která je vždy true, což znamená, že se vykoná řádek 3. Pokud však podmínka 1 bude nepravdivá, přejdeme na řádek označený číslem 4, kde také podmínka je vždy true, což znamená, že se vykoná řádek 5. Takže podmínky označené komentářem 0 nikdy nebudou splněny, což nemusí být zcela logika, na kterou programátor spoléhá.
Varování N5
Varování analyzátoru: [CWE-783] Možná, že operator ‘?:’ funguje jinak, než se očekávalo. Jeho priorita je nižší než priorita jiných operátorů v jeho podmínce. 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);
}
}Diagnostika spustila na řádek:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Jelikož proměnná j o několik řádků výše se inicializuje na nulu, ternární operátor vrátí hodnotu false. Kvůli této podmínce se tělo smyčky vykoná pouze jednou. Domnívám se, že tento úryvek kódu nefunguje tak, jak programátor zamýšlel.
Upozornění N6
Varování analyzátoru: [CWE-571] Výraz 'installedPackageVersions.Count != 1' je vždy pravdivý. NugetService.cs 1405
private void remove_nuget_cache_for_package(....)
{
if (!config.AllVersions && installedPackageVersions.Count > 1)
{
const string allVersionsChoice = "Všechny verze";
if (installedPackageVersions.Count != 1)
{
choices.Add(allVersionsChoice);
}
....
}
....
}Zde je zvláštní vnořená podmínka: installedPackageVersions.Count != 1, která bude vždy true. Takové varování často naznačuje logickou chybu v kódu, a v ostatních případech pouze nadbytečnou kontrolu.
Upozornění N7
Varování analyzátoru: Existují identické podvýrazy 'commandArguments.contains("-apikey")' nalevo a napravo od operátoru '||'. 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");
}Programátor, který napsal tento úsek kódu, zkopíroval poslední dvě řádky a zapomněl je upravit. Kvůli tomu uživatelé Chocolatey ztratili možnost použít parametr apikey ještě dalším způsobem. Podobně jako parametry výše mohu navrhnout následující varianty:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Chyby z kopírování a vkládání mají velkou šanci se dříve nebo později objevit v jakémkoli projektu s velkým množstvím zdrojového kódu, a jedním z nejlepších prostředků boje proti nim je statická analýza.
P.S. A jako vždy, tato chyba má tendenci se objevit na konci vícerozměrné podmínky :). Viz publikaci "".
Upozornění N8
Varování analyzátoru: [CWE-476] Objekt 'installedPackage' byl použit, než byl ověřen na null. Zkontrolujte řádky: 910, 917. NugetService.cs 910
public virtual 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)
{
....
}
....
}Klasická chyba: objekt je nejprve použit a poté kontrolován installedPackage na null. Tato diagnostika nám říká o jednom ze dvou problémů v programu: buď installedPackage nikdy není null, což je pochybné, a pak je kontrola nadbytečná, nebo potenciálně můžeme získat vážnou chybu v kódu – pokus o přístup na nulový odkaz.
Závěr
A máme za sebou ještě jeden malý krok – nyní je používání PVS-Studio ještě jednodušší a pohodlnější. Také chci říci, že Chocolatey je dobrý správce balíčků s malým počtem chyb v kódu, kterých by mohlo být ještě méně při používání PVS-Studio.
Zveme vás a vyzkoušet PVS-Studio. Pravidelné používání statického analyzátoru zlepší kvalitu a spolehlivost kódu vyvíjeného vaším týmem a pomůže předejít mnoha .
P.S.
Před publikováním jsme zaslali článek vývojářům Chocolatey, a ti jej dobře přijali. Nic kritického jsme nenalezli, ale jim se například líbila chyba, kterou jsme našli, týkající se klíče „api-key“.
Pokud chcete sdílet tento článek s anglicky mluvícím publikem, prosím použijte odkaz na překlad: Vladislav Stolyarov. .
Zdroj: habr.com
