PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps
Continuăm să facem utilizarea PVS-Studio mai confortabilă. Acum, analizorul nostru este disponibil în Chocolatey, managerul de pachete pentru Windows. Credem că acest lucru va facilita desfășurarea PVS-Studio, în special în serviciile de cloud. Pentru a nu ne abate, să verificăm codul sursă al aceluiași Chocolatey. Sistemul CI va fi Azure DevOps.

Iată lista altor articole de-ale noastre pe tema integrării cu sistemele de cloud:

Recomand să acordați atenție primului articol despre integrarea cu Azure DevOps, deoarece în acest caz au fost omise anumite aspecte pentru a nu se repeta.

Așadar, eroii acestui articol sunt:

PVS-Studio — un instrument de analiză statică a codului, destinat identificării erorilor și vulnerabilităților potențiale în programele scrise în limbajele C, C++, C# și Java. Funcționează pe sisteme de 64 de biți pe Windows, Linux și macOS și poate analiza cod destinat platformelor ARM de 32 de biți, 64 de biți și încorporate. Dacă încercați pentru prima dată analiza statică a codului pentru a verifica proiectele dumneavoastră, vă recomandăm să consultați articolul despre cum să vizualizați rapid cele mai interesante avertizări PVS-Studio și să evaluați posibilitățile acestui instrument.

Azure DevOps — un set de servicii cloud care acoperă în mod colaborativ întregul proces de dezvoltare. Această platformă include instrumente precum Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, care permit accelerarea procesului de creare a software-ului și îmbunătățirea calității acestuia.

Chocolatey – un manager de pachete pentru Windows cu sursă deschisă. Scopul proiectului este de a automatiza întregul ciclu de viață al software-ului, de la instalare la actualizare și ștergere în sistemele de operare Windows.

Despre utilizarea Chocolatey

Puteți vedea cum să instalați managerul de pachete vizitând această linkul. Documentația completă pentru instalarea analizorului este disponibilă la linkul în secțiunea „Instalare folosind managerul de pachete Chocolatey”. Pe scurt, voi repeta câteva puncte de acolo.

Comanda pentru instalarea celei mai recente versiuni a analizorului:

choco install pvs-studio

Comanda pentru instalarea unei versiuni specifice a pachetului PVS-Studio:

choco install pvs-studio --version=7.05.35617.2075

Implicit este instalat doar nucleul analizei - componenta Core. Toate celelalte flag-uri (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) pot fi transmise prin intermediul —package-parameters.

Exemplu de comandă care va instala analiza cu plugin pentru Visual Studio 2019:

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

Acum să ne uităm la un exemplu de utilizare ușoară a analizei sub Azure DevOps.

Configurare

Îmi amintesc că pentru aspecte precum înregistrarea contului, crearea unui Build Pipeline și sincronizarea contului cu proiectul din repository-ul de pe GitHub, există un ghid separat. articol. Configurarea noastră va începe direct cu scrierea fișierului de configurare.

Pentru început, să configurăm trigger-ul de pornire, specificând că facem pornirea doar pentru modificările din master ramura:

trigger:
- master

Următoarea etapă este să alegem mașina virtuală. Deocamdată, aceasta va fi un agent Microsoft-hosted cu Windows Server 2019 și Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Să trecem la corpul fișierului de configurare (bloc steps). Deși nu putem instala software arbitrar pe mașina virtuală, nu am adăugat un container Docker. Putem adăuga Chocolatey ca extensie pentru Azure DevOps. Pentru aceasta, accesați linkul. Apăsați Get it free. Apoi, dacă sunteți deja autentificat, alegeți contul dvs., iar dacă nu, faceți aceleași lucruri după autentificare.

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Aici trebuie să selectați unde vom adăuga extensia și să apăsați butonul Instalează.

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

După instalare, apăsați Proceed to organization:

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Acum putem vedea un șablon pentru sarcina Chocolatey în fereastra tasks când edităm fișierul de configurare azure-pipelines.yml:

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Apăsați pe Chocolatey și veți vedea lista de câmpuri:

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Aici trebuie să alegem install în câmpul cu comenzi. În Nuspec File Name vom indica numele pachetului necesar - pvs-studio. Dacă nu specificați versiunea, se va instala ultima, ceea ce ne satisface pe deplin. Apăsați pe butonul add și vom vedea sarcina generată în fișierul de configurare.

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

Apoi să trecem la partea principală a fișierului nostru:

- task: CmdLine@2
  inputs:
    script: 

Acum trebuie să creăm un fișier cu licența analizei. Aici PVSNAME și PVSKEY – sunt denumirile variabilelor, valorile cărora le vom specifica în setări. Acestea vor păstra login-ul și cheia de licență PVS-Studio. Pentru a le seta valorile, deschideți meniul Variables->New variable. Să creăm variabilele PVSNAME pentru login și PVSKEY pentru cheia analizoarelor. Nu uitați să bifați căsuța Păstrați acest valoare secretă pentru PVSKEY. Codul comenzii:

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

Să compilăm proiectul folosind fișierul bat aflat în repository:

call build.bat

Vom crea un folder unde vor fi stocate fișierele cu rezultatele analizei:

call mkdir PVSTestResults

Vom lansa analiza proiectului:

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

Vom converti raportul nostru în format html folosind utilitarul PlogConverter:

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

Acum trebuie să creăm o sarcină pentru a putea exporta raportul.

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

Fișierul complet de configurare arată astfel:

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

Să facem clic Save->Save->Run pentru a lansa sarcina. Vom exporta raportul accesând tab-ul sarcinii.

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Proiectul Chocolatey conține în total 37615 linii de cod C#. Să analizăm câteva din erorile identificate.

Rezultatele verificării

Avertisment N1

Avertisment al analizoarei: V3005 Variabila 'Provider' este asignată ei însăși. CrytpoHashProviderSpecs.cs 38

public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
  ....
  protected CryptoHashProvider Provider;
  ....
  public override void Context()
  {
    Provider = Provider = new CryptoHashProvider(FileSystem.Object);
  }
}

Analizorul a descoperit o atribuire a variabilei către sine, ceea ce nu are sens. Probabil că în locul uneia dintre aceste variabile ar trebui să existe o altă variabilă. Sau ar putea fi o greșeală de tipar, iar atribuirea suplimentară poate fi pur și simplu ștearsă.

Avertisment N2

Avertisment al analizoarei: V3093 [CWE-480] Operatorul '&' evaluează ambele operande. Poate ar trebui utilizat în schimb operatorul de scurtcircuit '&&'. 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;
  }
}

Diferența operatorului & de la operator && constă în faptul că, dacă partea stângă a expresiei — false, va fi totuși calculată partea dreaptă, ceea ce în acest caz implică apeluri redundante ale metodei system.directory_exists.

În fragmentul analizat, acesta este o mică neatenție. Da, această condiție poate fi optimizată, înlocuind operatorul & cu operatorul &&, dar, din punct de vedere practic, acest lucru nu afectează nimic. Totuși, în alte cazuri, confuzia între & și && poate provoca probleme serioase, când partea dreaptă a expresiei va lucra cu valori incorecte/invalid. De exemplu, în colecția noastră de erori, identificate cu ajutorul diagnosticului V3093, există un astfel de caz:

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

Chiar dacă indexul k este incorect, va fi folosit pentru a accesa un element al array-ului. Ca rezultat, va fi generată o excepție IndexOutOfRangeException.

Avertizările N3, N4

Avertisment al analizoarei: V3022 [CWE-571] Expresia ‘shortPrompt’ este întotdeauna adevărată. InteractivePrompt.cs 101
Avertisment al analizoarei: V3022 [CWE-571] Expresia ‘shortPrompt’ este întotdeauna adevărată. 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
    ....
  }
  ....
}

În acest caz există o logică ciudată a operatorului ternar. Să analizăm mai în detaliu: dacă se îndeplinește condiția marcată cu cifra 1, atunci vom trece la condiția 2, care este întotdeauna true, ceea ce înseamnă că se va executa linia 3. Dacă, totuși, condiția 1 se dovedește a fi falsă, atunci vom trece la linia marcată cu cifra 4, condiția care de asemenea este întotdeauna true, ceea ce înseamnă că se va executa linia 5. Astfel, condițiile marcate cu comentariul 0 nu vor fi niciodată îndeplinite, ceea ce poate reprezenta o logică de lucru diferită de cea la care se aștepta programatorul.

Avertizarea N5

Avertisment al analizoarei: V3123 [CWE-783] Poate că operatorul ‘?:’ funcționează într-un mod diferit decât era așteptat. Prioritatea sa este mai mică decât prioritatea celorlalte operații din condiția sa. 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);
  }
}

Diagnosticul a fost activat pe linia:

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

Întrucât variabila j este inițializată cu zero cu câteva linii mai sus, operatorul ternar va returna valoarea false. Din cauza acestei condiții, corpul buclei va fi executat doar o singură dată. Mi se pare că acest fragment de cod nu funcționează deloc așa cum a intenționat programatorul.

Avertizare N6

Avertisment al analizoarei: V3022 [CWE-571] Expresia ‘installedPackageVersions.Count != 1’ este întotdeauna adevărată. NugetService.cs 1405

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

Aici este o condiție înfășurată ciudată: installedPackageVersions.Count != 1, care va fi întotdeauna true. Adesea, astfel de avertizări indică o eroare logică în cod, iar în alte cazuri pur și simplu o verificare redundantă.

Avertizare N7

Avertisment al analizoarei: V3001 Există sub-expresii identice ‘commandArguments.contains("-apikey")’ pe stânga și pe dreapta operatorului ‘||’. 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");
}

Programatorul care a scris acest cod a copiat ultimele două linii și a uitat să le editeze. Din această cauză, utilizatorii Chocolatey au fost privați de posibilitatea de a aplica parametrul apikey în încă câteva moduri. Similar parametrilor de mai sus, pot sugera următoarele variante:

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

Erorile de tip copiere și lipire au o șansă mare de a apărea mai devreme sau mai târziu în orice proiect cu un volum mare de cod sursă, iar unul dintre cele mai bune instrumente pentru a lupta împotriva lor este analiza statică.

P.S. Și, ca întotdeauna, această eroare tinde să apară la sfârșitul unei condiții multi-linie :). Vezi publicația "Efectul ultimei linii".

Avertizare N8

Avertisment al analizoarei: V3095 [CWE-476] Obiectul ‘installedPackage’ a fost folosit înainte de a fi verificat împotriva null. Verificați liniile: 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)
  {
    ....
  }
  ....
}

Eroare clasică: obiectul este utilizat mai întâi installedPackage și apoi verificat pentru null. Această diagnosticare ne indică una dintre cele două probleme în program: fie installedPackage nu este niciodată egal cu null, ceea ce este îndoielnic, și atunci verificarea este redundantă, fie putem obține o eroare gravă în cod – o încercare de accesare a unei referințe nule.

Concluzie

Iată că am realizat un alt pas mic – acum utilizarea PVS-Studio a devenit și mai simplă și mai convenabilă. De asemenea, vreau să spun că Chocolatey este un bun manager de pachete cu un număr mic de erori în cod, care ar putea să fie și mai puține cu utilizarea PVS-Studio.

Vă invităm descărcați să încercați PVS-Studio. Utilizarea regulată a analizatorului static va îmbunătăți calitatea și fiabilitatea codului dezvoltat de echipa dumneavoastră și va ajuta la prevenirea multor vulnerabilități de tip zero-day.

P.S.

Înainte de publicare, am trimis articolul dezvoltatorilor Chocolatey, iar aceștia l-au primit bine. Nu am găsit nimic critic, dar, de exemplu, le-a plăcut eroarea pe care am identificat-o legată de cheia „api-key”.

PVS-Studio acum în Chocolatey: verificarea Chocolatey din Azure DevOps

Dacă doriți să împărtășiți acest articol cu o audiență anglofonă, vă rog să folosiți linkul la traducere: Vladislav Stolyarov. PVS-Studio este acum în Chocolatey: Verificând Chocolatey sub Azure DevOps.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster