
Pokračujeme v zlepšovaní používania PVS-Studio. Náš analyzátor je teraz k dispozícii v Chocolatey, správcu balíkov pre Windows. Veríme, že to uľahčí nasadenie PVS-Studio, najmä v cloudových službách. Aby sme sa nemuseli ďalej zaoberať, skontrolujeme zdrojový kód toho istého Chocolatey. Ako CI systém použijeme Azure DevOps.
Tu je zoznam našich ďalších článkov na tému integrácie s cloudovými systémami:
Odporúčam venovať pozornosť prvému článku o integrácii s Azure DevOps, pretože niektoré body sú v tomto prípade vynechané, aby nedochádzalo k duplicite.
Teda, hrdinovia tohto článku:
— nástroj statickej analýzy kódu, určený na odhaľovanie chýb a potenciálnych zraniteľností v programoch napísaných v jazykoch C, C++, C# a Java. Funguje v 64-bitových systémoch na Windows, Linux a macOS, a môže analyzovať kód určený pre 32-bitové, 64-bitové a vstavané ARM platformy. Ak sa chystáte prvýkrát vyskúšať statickú analýzu kódu na kontrolu svojich projektov, odporúčame oboznámiť sa so o tom, ako rýchlo získate prehľad o najzaujímavejších upozorneniach PVS-Studio a ocenite možnosti tohto nástroja.
— súbor cloudových služieb, ktoré spoločne pokrývajú celý proces vývoja. Táto platforma zahŕňa nástroje ako Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, ktoré umožňujú urýchliť proces vytvárania softvéru a zlepšiť jeho kvalitu.
— správca balíkov pre Windows s otvoreným zdrojovým kódom. Cieľom projektu je automatizovať celý životný cyklus softvéru od inštalácie po aktualizáciu a odinštalovanie v operačných systémoch Windows.
O používaní Chocolatey
Zistiť, ako nainštalovať samotného správcu balíkov, môžete podľa tejto . Kompletná dokumentácia k inštalácii analyzátora je dostupná na v sekcii „Inštalácia pomocou správcu balíkov Chocolatey“. Stručne zopakujem niektoré body odtiaľ.
Príkaz na inštaláciu poslednej verzie analyzátora:
choco install pvs-studioPríkaz na inštaláciu konkrétnej verzie balíka PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Predvolene sa inštaluje iba jadro analyzátora — komponent Core. Všetky ostatné flagy (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) je možné odovzdať pomocou —package-parameters.
Príklad príkazu, ktorý nainštaluje analyzátor s pluginom pre Visual Studio 2019:
choco install pvs-studio --package-parameters="'/MSVS2019'"Teraz si pozrime príklad pohodlného použitia analyzátora pod Azure DevOps.
Nastavenie
Pripomínam, že o takých otázkach, ako je registrácia účtu, vytvorenie Build Pipeline a synchronizácia účtu s projektom uloženým v repozitári na GitHub, existuje osobný . Naša konfigurácia sa hneď začne písaním konfiguračného súboru.
Na začiatok nastavíme spúšťací trigger, pričom uvedieme, že spúšťame iba pre zmeny v master vetve:
trigger:
- masterĎalej musíme vybrať virtuálny stroj. V súčasnosti to bude Microsoft-hosted agent s Windows Server 2019 a Visual Studio 2019:
pool:
vmImage: 'windows-latest'Prejdeme k telu konfiguračného súboru (blok steps). Napriek tomu, že na virtuálny stroj nemožno nainštalovať ľubovoľný softvér, nepridal som Docker kontajner. Môžeme pridať Chocolatey ako rozšírenie pre Azure DevOps. Na to prejdeme na . Klikneme Získať zadarmo. Ďalej, ak ste už prihlásení, jednoducho vyberte svoj účet, a ak nie, vykonajte všetko rovnaké po prihlásení.

Tu musíme vybrať, kam pridáme rozšírenie, a stlačiť tlačidlo Nainštalovať.

Po úspešnej inštalácii klikneme na Pokračovať k organizácii:

Teraz môžeme vidieť šablónu pre úlohu Chocolatey v okne tasks pri úprave konfiguračného súboru azure-pipelines.yml:

Klikneme na Chocolatey a uvidíme zoznam polí:

Tu musíme vybrať install v poli s príkazmi. V Nuspec File Name uvedieme názov požadovaného balíka – pvs-studio. Pokiaľ nie je zadaná verzia, nainštaluje sa posledná, čo nás úplne uspokojuje. Klikneme na tlačidlo add a uvidíme vytvorenú úlohu v konfiguračnom súbore.
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'Ďalej prejdeme k hlavnej časti nášho súboru:
- task: CmdLine@2
inputs:
script: Teraz musíme vytvoriť súbor s licenciou analyzátora. Tu PVSNAME a PVSKEY – názvy premenných, hodnoty ktorých uvádzame v nastaveniach. Budú uchovávať prihlásenie a licenčný kľúč PVS-Studio. Aby sme nastavili ich hodnoty, otvoríme menu Variables->New variable. Vytvoríme premenné PVSNAME pre prihlásenie a PVSKEY pre analýzu kľúča. Nezabudnite zaškrtnúť Túto hodnotu uchovajte v tajnosti pre PVSKEY. Príkazový kód:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)Zostavíme projekt pomocou bat súboru, ktorý sa nachádza v repozitári:
call build.batVytvoríme priečinok, kde budú uložené súbory s výsledkami práce analyzátora:
call mkdir PVSTestResultsSpustíme analýzu projektu:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog Prevedieme našu správu do formátu html pomocou nástroja PlogConverter:
call "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogTeraz musíme vytvoriť úlohu, aby sme mohli exportovať správu.
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()Celý konfiguračný súbor vyzerá 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()Klikneme Uložiť->Uložiť->Spustiť na spustenie úlohy. Exportujeme správu vstupom do záložky úloh.
Projekt Chocolatey obsahuje celkovo 37615 riadkov kódu C#. Pozrime sa na niektoré z nájdených chýb.
Výsledky kontroly
Varovanie N1
Upozornenie analyzátora: Premenná 'Provider' je priradená sama sebe. CrytpoHashProviderSpecs.cs 38
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}Analyzátor odhalil priradenie premennej samej sebe, čo nedáva zmysel. Pravdepodobne by na mieste jednej z týchto premenných mala byť nejaká iná. Alebo je to preklep a nadbytočné priradenie sa dá jednoducho odstrániť.
Varovanie N2
Upozornenie analyzátora: [CWE-480] Operátor '&' vyhodnocuje oba operandy. Namiesto toho by sa mal použiť skrátený 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;
}
}Rozdiel medzi operátorom & a operátorom && je v tom, že ak je ľavá časť výrazu — nepravda, ale stále sa vypočíta pravá strana, čo v tomto prípade naznačuje zbytočné volania metódy system.directory_exists.
V uvedenom fragmente ide o drobnú chybu. Áno, túto podmienku je možné optimalizovať, ak zameníme operátor & za operátor &&, ale z praktického hľadiska to nič nezmení. Avšak v iných prípadoch môže zamieňanie medzi & a && spôsobovať vážne problémy, keď pravá strana výrazu bude pracovať s neplatnými/zakázanými hodnotami. Napríklad v našej zbierke chýb, , existuje takýto prípad:
if ((k < nct) & (s[k] != 0.0))Aj keď je index k neplatný, bude sa používať na prístup k elementu poľa. V dôsledku toho sa vygeneruje výnimka IndexOutOfRangeException.
Varovania N3, N4
Upozornenie analyzátora: [CWE-571] Výraz 'shortPrompt' je vždy pravdivý. InteractivePrompt.cs 101
Upozornenie analyzátora: [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 prípade je prítomná zvláštna logika fungovania ternárneho operátora. Pozrime sa na to podrobnejšie: ak sa splní podmienka, ktorú som označil číslom 1, prejdeme na podmienku 2, ktorá je vždy true, čo znamená, že sa vykoná riadok 3. Ak však bude podmienka 1 nepravdivá, prejdeme na riadok označený číslom 4, pričom podmienka tam bude tiež vždy true, čo znamená, že sa vykoná riadok 5. Takto podmienky, označené komentárom 0, nikdy nebudú vykonané, čo nemusí byť úplne to, na čo programátor počítal.
Varovanie N5
Upozornenie analyzátora: [CWE-783] Možno operátor '?:' funguje inak, ako sa očakávalo. Jeho priorita je nižšia ako priorita iných operátorov v jeho podmienke. 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 reagovala na riadok:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Keďže premenná j Na niekoľkých riadkoch vyššie je inicializovaný na nulu, ternárny operátor vráti hodnotu nepravda. Kvôli tejto podmienke sa telo cyklu vykoná len raz. Mám pocit, že tento fragment kódu nefunguje tak, ako si programátor predsavzal.
Upozornenie N6
Upozornenie analyzátora: [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šetky verzie";
if (installedPackageVersions.Count != 1)
{
choices.Add(allVersionsChoice);
}
....
}
....
}Tu je podivná vnorená podmienka: installedPackageVersions.Count != 1, ktorá bude vždy true. Často takáto výstraha naznačuje logickú chybu v kóde alebo v ostatných prípadoch len nadbytočné overenie.
Upozornenie N7
Upozornenie analyzátora: Existujú identické podvýrazy 'commandArguments.contains("-apikey")' na ľavej a pravej strane operátora '||'. 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, ktorý napísal túto časť kódu, skopíroval posledné dve riadky a zabudol ich upraviť. V dôsledku toho používatelia Chocolatey stratili možnosť použiť parameter apikey ešte niekoľkými spôsobmi. Rovnako ako predchádzajúce parametre, môžem navrhnúť takéto možnosti:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Chyby kopírovania a vkladania majú vysokú pravdepodobnosť, že sa skôr alebo neskôr objavia v akomkoľvek projekte s veľkým množstvom pôvodného kódu, a jedným z najlepších prostriedkov na boj proti nim je statická analýza.
P.S. A ako vždy, táto chyba má tendenciu objavovať sa na konci viacriadkových podmienok :). Pozrite si publikáciu "".
Upozornenie N8
Upozornenie analyzátora: [CWE-476] Objekt 'installedPackage' bol použitý predtým, ako bol overený proti null. Skontrolujte riadky: 910, 917. NugetService.cs 910
verejný virtuálny ConcurrentDictionary<string, PackageResult> get_outdated(....)
{
....
var pinnedPackageResult = outdatedPackages.GetOrAdd(
packageName,
new PackageResult(installedPackage,
_fileSystem.combine_paths(
ApplicationParameters.PackagesLocation,
installedPackage.Id));
....
ak ( installedPackage != null
&& !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion)
&& !config.UpgradeCommand.ExcludePrerelease)
{
....
}
....
}Klasická chyba: najprv sa objekt installedPackage používa, a potom sa kontroluje na null. Táto diagnostika nám hovorí o jednom z dvoch problémov v programe: buď installedPackage nikdy nie je rovný null, čo je pochybné, a potom je kontrola nadbytočná, alebo môžeme potenciálne získať vážnu chybu v kóde – pokus o prístup cez nulovú referenciu.
Záver
Tak sme spravili ďalší malý krok – teraz je používanie PVS-Studio ešte jednoduchšie a pohodlnejšie. Rád by som povedal, že Chocolatey je dobrý správca balíkov s malým počtom chýb v kóde, ktorých by mohlo byť ešte menej pri použití PVS-Studio.
Pozývame a vyskúšať PVS-Studio. Pravidelné používanie statického analyzátora zlepší kvalitu a spoľahlivosť kódu vyvíjaného vaším tímom a pomôže predchádzať mnohým .
P.S.
Pred publikáciou sme poslali článok vývojárom Chocolatey, a tí ho dobre prijali. Nič kritické sme nenalezli, ale im sa napríklad páčila námi objavená chyba súvisiaca s kľúčom "api-key".
Ak chcete zdieľať tento článok s anglicky hovoriacim publikom, prosím, použite odkaz na preklad: Vladislav Stolyarov. .
Zdroj: habr.com
