
Nenehajamo, da bi uporaba PVS-Studio postala lažja. Zdaj je naš analizator na voljo v Chocolatey, upravitelju paketov za Windows. Prepričani smo, da bo to olajšalo uvedbo PVS-Studio, zlasti v oblačnih storitvah. Da se ne bi oddaljevali, bomo preverili izvorno kodo istega Chocolatey-ja. Azure DevOps bo služil kot CI sistem.
Tukaj je seznam naših drugih člankov o integraciji z oblačnimi sistemi:
Priporočam, da se osredotočite na prvi članek o integraciji z Azure DevOps, ker so nekatere točke v tem primeru izpuščene, da se ne bi ponavljali.
Torej, junaki tega članka:
— orodje za statično analizo kode, namenjeno odkrivanju napak in potencialnih ranljivosti v programih, napisanih v C, C++, C# in Java. Deluje v 64-bitnih sistemih na Windows, Linux in macOS ter lahko analizira kodo, namenjeno 32-bitnim, 64-bitnim in vgrajenim ARM platformam. Če prvič preizkušate statično analizo kode za preverjanje svojih projektov, priporočamo, da se seznanite s o tem, kako hitro pregledati najzanimivejša opozorila PVS-Studio in oceniti možnosti tega orodja.
— nabor oblačnih storitev, ki pokrivajo celoten proces razvoja. Ta platforma vključuje orodja, kot so Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, ki omogočajo pospešitev procesa razvoja programske opreme in izboljšanje njene kakovosti.
– upravitelj paketov za Windows z odprto kodo. Cilj projekta je avtomatizirati celoten življenjski cikel programske opreme od namestitve do posodabljanja in odstranjevanja v operacijskih sistemih Windows.
O uporabi Chocolatey
Oglejte si, kako namestiti sam upravitelj paketov, lahko na tej . Popolna dokumentacija za namestitev analizatorja je na voljo na v razdelku „Namestitev z uporabo upravitelja paketov Chocolatey“. Na kratko bom ponovil nekatere točke od tam.
Ukaz za namestitev zadnje različice analizatorja:
choco install pvs-studioUkaz za namestitev določene različice paketa PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Privzeto se namesti samo jedro analizatorja — komponenta Core. Vse ostale oznake (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) je mogoče posredovati z uporabo —package-parameters.
Primer ukaza, ki bo namestil analizator s dodatkom za Visual Studio 2019:
choco install pvs-studio --package-parameters="'/MSVS2019'"Sedaj poglejmo primer enostavne uporabe analizatorja v Azure DevOps.
Setup
Spomnim, da so za take zadeve, kot so registracija računa, ustvarjanje Build Pipeline in sinhronizacija računa s projektom v repozitoriju na GitHubu, na voljo posebne . Naša nastavitev se bo takoj začela s pisanjem konfiguracijske datoteke.
Najprej nastavimo sprožilec, tako da določimo, da se sprožitev izvede samo za spremembe v master veji:
trigger:
- masterNato moramo izbrati virtualni stroj. V tem trenutku bo to Microsoftov gostujoči agent z Windows Server 2019 in Visual Studio 2019:
pool:
vmImage: 'windows-latest'Poglejmo telo konfiguracijske datoteke (blok steps). Kljub temu, da na virtualni stroj ni mogoče namestiti poljubne programske opreme, nisem dodal Docker kontejnerja. Chocolatey lahko dodamo kot razširitev za Azure DevOps. Zato gremo na . Kliknemo Get it free. Če ste že prijavljeni, preprosto izberite svoj račun, sicer pa naredite vse to po prijavi.

Tukaj je treba izbrati, kam dodamo razširitev, in pritisniti gumb Namestite.

Po uspešni namestitvi kliknite Proceed to organization:

Zdaj lahko vidite predlogo za nalogo Chocolatey v oknu naloge med urejanjem konfiguracijske datoteke azure-pipelines.yml:

Kliknimo na Chocolatey in videli bomo seznam polj:

Tukaj moramo izbrati install v polju z ukazi. V Nuspec File Name bomo navedli ime potrebnega paketa – pvs-studio. Če ne navedemo različice, se bo namestila najnovejša, kar nas povsem zadostuje. Kliknite na gumb add in videli bomo oblikovano nalogo v konfiguracijski datoteki.
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'Nato se premaknemo na glavni del naše datoteke:
- task: CmdLine@2
inputs:
script: Sedaj moramo ustvariti datoteko z licenco analizatorja. Tukaj PVSNAME in PVSKEY – sta imeni spremenljivk, katerih vrednosti bomo navedli v nastavitvah. Shranjevala bosta uporabniško ime in licenčni ključ PVS-Studio. Da določimo njihovo vrednost, odprite meni Variables->New variable. Ustvarimo spremenljivke PVSNAME za uporabniško ime in PVSKEY za ključa analize. Ne pozabite označiti kljukice Ohranite to vrednost v skrivnosti za PVSKEY. Ukazna koda:
kličite "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)Zgradimo projekt s pomočjo bat datoteke, ki se nahaja v repozitoriju:
kličite build.batUstvarimo mapo, kjer bodo shranjene datoteke z rezultati dela analizerja:
kličite mkdir PVSTestResultsZagnali bomo analizo projekta:
kličite "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog Pretvorimo našo poročilo v html format s pomočjo orodja PlogConverter:
kličite "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogSedaj je potrebno ustvariti nalogo, da lahko izvozimo poročilo.
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()Celotna konfiguracijska datoteka izgleda takole:
trigger:
- master
pool:
vmImage: 'windows-latest'
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'
- task: CmdLine@2
inputs:
script: |
kličite "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
credentials –u $(PVSNAME) –n $(PVSKEY)
kličite build.bat
kličite mkdir PVSTestResults
kličite "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
kličite "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o .PVSTestResults .PVSTestResultsChoco.plog
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()Pritisnite Shrani->Shrani->Zagon za zagon naloge. Izvozimo poročilo, ko preidemo na zavihek naloge.
Projekt Chocolatey vsebuje skupno 37615 vrstic C# kode. Oglejmo si nekatere od najdenih napak.
Rezultati preverjanja
Предупреждение N1
Opozorilo analizerja: Spremenljivka 'Provider' je dodeljena sama sebi. CrytpoHashProviderSpecs.cs 38
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}Analizator je odkril dodelitev spremenljivki sami sebi, kar nima smisla. Verjetno bi na mestu ene od teh spremenljivk morala biti neka druga. Morda gre za tipkarsko napako in nepotrebno dodelitev lahko enostavno odstranimo.
Предупреждение N2
Opozorilo analizerja: [CWE-480] Operator '&' ovrednoti oba operanda. Morda bi bilo bolje uporabiti skrajšani operator '&&'. 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;
}
}Razlika med operatorjem & in operatorjem && je v tem, da če je leva stran izraza — false, vendar se bo kljub temu izračunala desna stran, kar v tem primeru pomeni nepotrebne klice metode system.directory_exists.
V obravnavanem fragmentu gre za manjšo napako. Da, to stanje je mogoče optimizirati, tako da operater & postavimo na operater &&, vendar praktično to ne vpliva na nič. Vendar pa v drugih primerih zmeda med & in && lahko povzroči resne težave, ko bo desna stran izraza obravnavala nepravilne/nedosledne vrednosti. Na primer, v naši zbirki napak, , imamo tak primer:
if ((k < nct) & (s[k] != 0.0))Tudi če je indeks k nepravilen, se bo uporabljal za dostop do elementa niza. Posledica bo generiranje izjeme IndexOutOfRangeException.
Opozorila N3, N4
Opozorilo analizerja: [CWE-571] Izraz 'shortPrompt' je vedno resničen. InteractivePrompt.cs 101
Opozorilo analizerja: [CWE-571] Izraz 'shortPrompt' je vedno resničen. 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 tem primeru je prisotna čudna logika delovanja ternega operatorja. Poglejmo podrobneje: če bo izpolnjeno stanje, označeno z mojo številko 1, bomo prešli na stanje 2, ki je vedno true, kar pomeni, da se bo izvajala vrstica 3. Če pa se izkaže, da je stanje 1 lažno, bomo prešli na vrstico, označeno s številko 4, kjer je stanje prav tako vedno true, kar pomeni, da se bo izvajala vrstica 5. Tako pogoji, označeni s komentarjem 0, nikoli ne bodo izpolnjeni, kar morda ni povsem logika, na katero je programer računal.
Opozorilo N5
Opozorilo analizerja: [CWE-783] Morda operator '?:' deluje drugače, kot je bilo pričakovano. Njegova prioriteta je nižja od prioritete drugih operatorjev v njegovem stanju. 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 je delovala na vrstico:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Ker je spremenljivka vrednost Nekaj vrstic višje se inicializira na nič, ternarni operator bo vrnil vrednost false. Zaradi te omejitve se telo zanke izvede le enkrat. Zdi se mi, da ta del kode ne deluje tako, kot je programer nameraval.
Opozorilo N6
Opozorilo analizerja: [CWE-571] Izraz ‘installedPackageVersions.Count != 1’ je vedno resničen. NugetService.cs 1405
private void remove_nuget_cache_for_package(....)
{
if (!config.AllVersions && installedPackageVersions.Count > 1)
{
const string allVersionsChoice = "Vse različice";
if (installedPackageVersions.Count != 1)
{
choices.Add(allVersionsChoice);
}
....
}
....
}Tukaj je čudna vgrajena omejitev: installedPackageVersions.Count != 1, ki bo vedno true. Pogosto takšno opozorilo kaže na logično napako v kodi, v drugih primerih pa na nepotrebno preverjanje.
Opozorilo N7
Opozorilo analizerja: Obstajajo identične podizrazitve ‘commandArguments.contains("-apikey")’ na levi in desni strani operatorja ‘||’. 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");
}Programer, ki je napisal ta del kode, je kopiral zadnje dve vrstici in pozabil, da ju uredil. Zelo zaradi tega so uporabniki Chocolatey izgubili možnost uporabe parametra apikey še na nekaj načinov. Podobno kot pri zgornjih parametrih, lahko predlagam take možnosti:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Napačne kopije in prilepi napake se lahko zgodijo ob katerikoli priložnosti v projektu z obsežno kodo, in eno najboljših orodij za boj proti njim je statična analiza.
P.S. In kot vedno, ta napaka običajno nastane na koncu večvrstne omejitve :). Oglejte si objavo "".
Opozorilo N8
Opozorilo analizerja: [CWE-476] Objekt ‘installedPackage’ je bil uporabljen, preden je bil preverjen proti null. Preverite vrstice: 910, 917. NugetService.cs 910
javni virtualni ConcurrentDictionary<string, PackageResult> 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)
{
....
}
....
}Klasična greška: prvo se koristi objekat installedPackage a zatim se proverava na null. Ova dijagnostika nam ukazuje na jedan od dva problema u programu: ili installedPackage nikada nije jednak null, što je sumnjivo, i tada je provera suvišna, ili potencijalno možemo dobiti ozbiljnu grešku u kodu – pokušaj pristupa nul referenci.
Zaključek
Eto, napravili smo još jedan mali korak – sada je korišćenje PVS-Studio još lakše i prijatnije. Takođe želim reći da je Chocolatey dobar paket menadžer sa malim brojem grešaka u kodu, kojih bi moglo biti još manje korišćenjem PVS-Studio.
Vabimo vas i isprobati PVS-Studio. Redovno korišćenje statičkog analizatora povećaće kvalitet i pouzdanost koda koji vaša ekipa razvija i pomoći u prevenciji mnogih .
P.S.
Pre objavljivanja, poslali smo članak programerima Shocolatey, i oni su ga dobro prihvatili. Nije pronađeno ništa kritično, ali im je, na primer, bila zanimljiva greška koju smo otkrili vezano za ključ "api-key".
Če želite to besedilo deliti z angleško govorečo publiko, vas prosim, da uporabite povezavo na prevod: Vladislav Stolyarov. .
Izvor: habr.com
