PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps
Nastavljamo činiti korištenje PVS-Studio praktičnijim. Naš analizator sada je dostupan u Chocolateyju, upravitelju paketa za Windows. Vjerujemo da će ovo olakšati implementaciju PVS-Studia, posebno u uslugama u oblaku. Kako ne bismo otišli daleko, provjerimo izvorni kod iste Chocolatey. Azure DevOps djelovat će kao CI sustav.

Ovdje je popis naših ostalih članaka na temu integracije sa sustavima u oblaku:

Savjetujem vam da obratite pozornost na prvi članak o integraciji s Azure DevOps, jer su u ovom slučaju neke točke izostavljene kako se ne bi duplicirale.

Dakle, junaci ovog članka:

PVS-Studio je statički alat za analizu koda dizajniran za prepoznavanje pogrešaka i potencijalnih ranjivosti u programima napisanim u C, C++, C# i Javi. Radi na 64-bitnim Windows, Linux i macOS sustavima i može analizirati kod dizajniran za 32-bitne, 64-bitne i ugrađene ARM platforme. Ako prvi put isprobavate statičku analizu koda kako biste provjerili svoje projekte, preporučujemo da se upoznate članak o tome kako brzo pregledati najzanimljivija PVS-Studio upozorenja i procijeniti mogućnosti ovog alata.

Azure DevOps — skup usluga u oblaku koji zajednički pokrivaju cijeli proces razvoja. Ova platforma uključuje alate kao što su Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, koji vam omogućuju da ubrzate proces stvaranja softvera i poboljšate njegovu kvalitetu.

chocolatey je upravitelj paketa otvorenog koda za Windows. Cilj projekta je automatizirati cijeli životni ciklus softvera od instalacije do ažuriranja i deinstalacije na Windows operativnim sustavima.

O korištenju Chocolateya

Ovdje možete vidjeti kako instalirati sam upravitelj paketa link. Kompletna dokumentacija za ugradnju analizatora dostupna je na link Pogledajte odjeljak Instalacija pomoću upravitelja paketa Chocolatey. Ukratko ću ponoviti neke točke odatle.

Naredba za instaliranje najnovije verzije analizatora:

choco install pvs-studio

Naredba za instaliranje određene verzije paketa PVS-Studio:

choco install pvs-studio --version=7.05.35617.2075

Prema zadanim postavkama instalirana je samo jezgra analizatora, Core komponenta. Sve ostale zastavice (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) mogu se proslijediti pomoću --package-parameters.

Primjer naredbe koja će instalirati analizator s dodatkom za Visual Studio 2019:

choco install pvs-studio --package-parameters="'/MSVS2019'"

Sada pogledajmo primjer praktične upotrebe analizatora pod Azure DevOps.

podešavanje

Dopustite mi da vas podsjetim da postoji zaseban odjeljak o takvim pitanjima kao što su registracija računa, stvaranje Build Pipeline-a i sinkronizacija vašeg računa s projektom koji se nalazi u GitHub repozitoriju. članak. Naše postavljanje će odmah započeti pisanjem konfiguracijske datoteke.

Prvo, postavimo okidač za pokretanje, koji pokazuje da pokrećemo samo za promjene majstor podružnica:

trigger:
- master

Zatim moramo odabrati virtualni stroj. Za sada će to biti agent koji hostira Microsoft sa sustavima Windows Server 2019 i Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Prijeđimo na tijelo konfiguracijske datoteke (block koraci). Unatoč činjenici da ne možete instalirati proizvoljan softver u virtualni stroj, nisam dodao Docker spremnik. Možemo dodati Chocolatey kao proširenje za Azure DevOps. Da bismo to učinili, idemo na link. Klik Oslobodite ga. Zatim, ako ste već autorizirani, jednostavno odaberite svoj račun, a ako niste, učinite istu stvar nakon autorizacije.

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Ovdje trebate odabrati gdje ćemo dodati proširenje i kliknuti gumb Instalirati.

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Nakon uspješne instalacije kliknite Prijeđite na organizaciju:

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Sada možete vidjeti predložak za Chocolatey zadatak u prozoru zadatke prilikom uređivanja konfiguracijske datoteke azure-pipelines.yml:

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Kliknite na Chocolatey i pogledajte popis polja:

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Ovdje moramo odabrati instalirati na terenu s timovima. U Nuspec naziv datoteke navedite naziv potrebnog paketa – pvs-studio. Ukoliko ne navedete verziju, instalirat će se najnovija, što nam u potpunosti odgovara. Pritisnimo tipku dodati i vidjet ćemo generirani zadatak u konfiguracijskoj datoteci.

steps:
- task: ChocolateyCommand@0
  inputs:
    command: 'install'
    installPackageId: 'pvs-studio'

Zatim, prijeđimo na glavni dio naše datoteke:

- task: CmdLine@2
  inputs:
    script: 

Sada moramo stvoriti datoteku s licencom analizatora. Ovdje PVSNAME и PVSKEY – imena varijabli čije vrijednosti navodimo u postavkama. Oni će pohraniti PVS-Studio prijavu i licencni ključ. Da biste postavili njihove vrijednosti, otvorite izbornik Varijable->Nova varijabla. Kreirajmo varijable PVSNAME za prijavu i PVSKEY za ključ analizatora. Ne zaboravite označiti kućicu Držite ovu vrijednost u tajnosti za PVSKEY. Šifra naredbe:

сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials 
–u $(PVSNAME) –n $(PVSKEY)

Izgradimo projekt koristeći bat datoteku koja se nalazi u repozitoriju:

сall build.bat

Kreirajmo mapu u kojoj će biti pohranjene datoteke s rezultatima analizatora:

сall mkdir PVSTestResults

Počnimo analizirati projekt:

сall "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog 

Naše izvješće pretvaramo u html format pomoću uslužnog programa PlogSonverter:

сall "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
–t html –o PVSTestResults .PVSTestResultsChoco.plog

Sada morate izraditi zadatak kako biste mogli prenijeti izvješće.

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

Kompletna konfiguracijska datoteka izgleda ovako:

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()

Kliknimo Spremi->Spremi->Pokreni za pokretanje zadatka. Preuzmite izvješće tako da odete na karticu zadataka.

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Projekt Chocolatey sadrži samo 37615 redaka C# koda. Pogledajmo neke od pronađenih pogrešaka.

Rezultati ispitivanja

Upozorenje N1

Upozorenje analizatora: V3005 Varijabla 'Provider' dodijeljena je 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 otkrio dodjelu varijable samom sebi, što nema smisla. Najvjerojatnije bi umjesto jedne od ovih varijabli trebala postojati neka druga. Pa, ili je ovo pogreška pri upisu, a dodatni zadatak se jednostavno može ukloniti.

Upozorenje N2

Upozorenje analizatora: V3093 [CWE-480] Operator '&' procjenjuje oba operanda. Možda bi se umjesto njega trebao koristiti kratkospojni operator '&&'. Platforma.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 između operatora & od operatera && je da ako je lijeva strana izraza lažan, tada će desna strana i dalje biti izračunata, što u ovom slučaju implicira nepotrebne pozive metode sustav.direktorij_postoji.

U razmatranom fragmentu ovo je manji nedostatak. Da, ovo se stanje može optimizirati zamjenom & operatora && operatorom, ali s praktičnog stajališta to ne utječe ni na što. Međutim, u drugim slučajevima, zabuna između & i && može uzrokovati ozbiljne probleme kada se desna strana izraza tretira s netočnim/nevažećim vrijednostima. Na primjer, u našoj zbirci pogrešaka, identificiran pomoću V3093 dijagnostike, postoji ovaj slučaj:

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

Čak i ako indeks k je netočan, koristit će se za pristup elementu niza. Kao rezultat toga, bit će bačena iznimka IndexOutOfRangeException.

Opomene N3, N4

Upozorenje analizatora: V3022 [CWE-571] Izraz 'shortPrompt' je uvijek istinit. InteractivePrompt.cs 101
Upozorenje analizatora: V3022 [CWE-571] Izraz 'shortPrompt' je uvijek istinit. 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
    ....
  }
  ....
}

U ovom slučaju postoji čudna logika iza rada ternarnog operatora. Pogledajmo pobliže: ako je uvjet koji sam označio brojem 1 ispunjen, tada ćemo prijeći na uvjet 2, koji je uvijek istinski, što znači da će se izvršiti linija 3. Ako se uvjet 1 pokaže netočnim, tada ćemo ići na liniju označenu brojem 4, uvjet u kojem je također uvijek istinski, što znači da će se izvršiti linija 5. Dakle, uvjeti označeni komentarom 0 nikada neće biti ispunjeni, što možda nije baš logika rada koju je programer očekivao.

Upozorenje N5

Upozorenje analizatora: V3123 [CWE-783] Možda operator '?:' radi na drugačiji način od očekivanog. Njegov prioritet je niži od prioriteta drugih operatora u njegovom stanju. Opcije.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 < nameStart.Length; ++i) 
  {
    int start, j = 0;
    do 
    {
      start = description.IndexOf (nameStart [i], j);
    } 
    while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false);
    ....
    return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
  }
}

Dijagnostika je radila za liniju:

while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)

Budući da varijabla j nekoliko redaka iznad inicijalizira na nulu, ternarni operator će vratiti vrijednost lažan. Zbog ovog uvjeta, tijelo petlje će se izvršiti samo jednom. Čini mi se da ovaj dio koda uopće ne radi kako je programer zamislio.

Upozorenje N6

Upozorenje analizatora: V3022 [CWE-571] Izraz 'installedPackageVersions.Count != 1' uvijek je istinit. NugetService.cs 1405

private void remove_nuget_cache_for_package(....)
{
  if (!config.AllVersions && installedPackageVersions.Count > 1)
  {
    const string allVersionsChoice = "All versions";
    if (installedPackageVersions.Count != 1)
    {
      choices.Add(allVersionsChoice);
    }
    ....
  }
  ....
}

Ovdje postoji čudno ugniježđeno stanje: InstallPackageVersions.Count != 1koji će uvijek biti istinski. Često takvo upozorenje ukazuje na logičku pogrešku u kodu, au drugim slučajevima jednostavno ukazuje na suvišnu provjeru.

Upozorenje N7

Upozorenje analizatora: V3001 Postoje identični podizrazi 'commandArguments.contains("-apikey")' lijevo i desno od '||' operater. 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 koji je napisao ovaj dio koda kopirao je i zalijepio zadnja dva retka i zaboravio ih urediti. Zbog toga korisnici Chocolateya nisu mogli primijeniti parametar apikey još par načina. Slično gore navedenim parametrima, mogu ponuditi sljedeće opcije:

commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");

Pogreške kopiranja i lijepljenja imaju velike šanse da se prije ili kasnije pojave u bilo kojem projektu s velikom količinom izvornog koda, a jedan od najboljih alata za borbu protiv njih je statička analiza.

PS Kao i uvijek, ova se pogreška obično pojavljuje na kraju uvjeta s više redaka :). Vidi publikaciju "Efekt zadnje linije".

Upozorenje N8

Upozorenje analizatora: V3095 [CWE-476] Objekt 'installedPackage' korišten je prije nego što je provjeren u odnosu na null. Provjerite linije: 910, 917. NugetService.cs 910

public virtual 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 pogreška: prvo prigovoriti instalirani paket koristi se i zatim provjerava nula. Ova nam dijagnostika govori o jednom od dva problema u programu: ili instalirani paket nikada jednaki nula, što je dvojbeno, a onda je provjera suvišna ili bismo potencijalno mogli dobiti ozbiljnu pogrešku u kodu - pokušaj pristupa nultoj referenci.

Zaključak

Stoga smo poduzeli još jedan mali korak - sada je korištenje PVS-Studio postalo još lakše i praktičnije. Također bih želio reći da je Chocolatey dobar upravitelj paketa s malim brojem grešaka u kodu, kojih bi moglo biti čak i manje ako koristite PVS-Studio.

Pozivamo vas preuzimanje i isprobajte PVS-Studio. Redovita uporaba statičkog analizatora poboljšat će kvalitetu i pouzdanost koda koji vaš tim razvija i pomoći u sprječavanju mnogih ranjivosti nultog dana.

PS

Prije objave članak smo poslali programerima Chocolateyja koji su ga dobro primili. Nismo našli ništa kritično, ali njima se, primjerice, svidjela greška koju smo pronašli vezana uz ključ "api-key".

PVS-Studio je sada u Chocolateyju: provjerava Chocolatey iz Azure DevOps

Ako želite podijeliti ovaj članak s publikom koja govori engleski, upotrijebite vezu za prijevod: Vladislav Stolyarov. PVS-Studio je sada u Chocolateyju: provjeravam Chocolatey pod Azure DevOps.

Izvor: www.habr.com

Dodajte komentar