PVS-Studio è ora in Chocolatey:verifica di Chocolatey in Azure DevOps

PVS-Studio è ora in Chocolatey:verifica di Chocolatey in Azure DevOps

Continuiamo a rendere più conveniente l'uso di PVS-Studio. Il nostro analizzatore è ora disponibile in Chocolatey, il gestore di pacchetti per Windows. Riteniamo che ciò semplificherà l'implementazione di PVS-Studio, in particolare nei servizi cloud. Quindi, subito, abbiamo anche controllato il codice sorgente dello stesso Chocolatey. Azure DevOps ha assunto il ruolo del sistema CI.

Ecco l'elenco dei nostri altri articoli sull'integrazione con il cloud:

  • PVS-Studio nei cloud:Azure DevOps
  • PVS-Studio tra le nuvole:CircleCI
  • PVS-Studio tra le nuvole:Travis CI
  • PVS-Studio tra le nuvole:GitLab CI/CD

Ti suggerisco di prestare attenzione al primo articolo sull'integrazione con Azure DevOps, poiché alcuni punti verranno omessi per evitare di ripetere alcune sfumature.

Quindi, i personaggi principali di questo articolo sono:

PVS-Studio è un analizzatore di codice statico per rilevare errori e potenziali vulnerabilità nel codice sorgente dei programmi, scritto in C, C++, C# e Java. Funziona in sistemi a 64 bit su Windows, Linux e macOS e può analizzare il codice per piattaforme ARM a 32 bit, 64 bit e embedded. Se è la prima volta che proverai l'analisi statica del codice per controllare i tuoi progetti, ti consigliamo di leggere l'articolo su come controllare rapidamente gli avvisi di PVS-Studio più interessanti e valutare le capacità di questo strumento.

Azure DevOps è un insieme di servizi cloud che coprono insieme l'intero processo di sviluppo. Questa piattaforma include strumenti come Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans per accelerare il processo di creazione del software e migliorarne la qualità.

Chocolatey è un gestore di pacchetti open source per Windows. L'obiettivo del progetto è automatizzare l'intero ciclo di vita del software, dall'installazione all'aggiornamento e all'eliminazione nei sistemi operativi Windows.

Informazioni sull'utilizzo di Chocolatey

Per vedere come installare il gestore pacchetti stesso, segui questo link. La documentazione completa sull'installazione dell'analizzatore è disponibile al link nella sezione "Installazione tramite il gestore di pacchetti Chocolatey". In poche parole, ripeterò alcuni dei punti da lì.

Il comando per l'installazione dell'ultima versione dell'analizzatore:

choco install pvs-studio

Il comando per l'installazione di un pacchetto PVS-Studio specifico:

choco install pvs-studio --version=7.05.35617.2075

Per impostazione predefinita, viene installato solo il core dell'analizzatore, ovvero il componente Core. Tutti gli altri flag (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) possono essere passati tramite --package-parameters.

Un esempio di comando che installerà l'analizzatore con il plug-in per Visual Studio 2019:

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

Ecco un esempio di comodo utilizzo dell'analizzatore in Azure DevOps.

Configurazione

Lascia che ti ricordi che l'articolo sopra menzionato fornisce tutte le informazioni necessarie su cose come la creazione di Build Pipeline e la sincronizzazione dell'account con il progetto in un repository GitHub. Nel nostro caso, la configurazione inizierà proprio con la scrittura di un file di configurazione.

Per cominciare, imposteremo un trigger di avvio, indicando che eseguiamo solo per le modifiche nel master filiale:

trigger:
- master

Successivamente, dobbiamo scegliere una macchina virtuale. A questo punto, sarà l'agente ospitato Microsoft con Windows Server 2019 e Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Passiamo al corpo del file di configurazione (i passaggi bloccare). Anche se non è possibile installare software casuale su una macchina virtuale, non ho aggiunto un contenitore Docker. Possiamo aggiungere Chocolatey come estensione per Azure DevOps. Per fare ciò, segui il link. Seleziona Scaricalo gratis . Inoltre, se hai già effettuato l'accesso, scegli il tuo account e, in caso contrario, fai lo stesso dopo l'autorizzazione.

Qui dobbiamo scegliere dove aggiungere l'estensione e fare clic su Installa .

Al termine dell'installazione, fai clic su Procedi all'organizzazione :

Ora puoi vedere il modello per l'attività Chocolatey nelle attività finestra durante la modifica del file di configurazione azure-pipelines.yml:

Fai clic su Chocolatey e vedrai l'elenco dei campi:

Qui dobbiamo scegliere installa in campo con i comandi. In Nome file Nuspec , specificare il nome del pacchetto necessario - pvs-studio. Senza la specifica della versione, verrà installata l'ultima versione, il che è assolutamente fantastico per noi. Fai clic su aggiungi e vedere la nuova attività nel file di configurazione.

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

Quindi, passiamo alla parte principale del nostro file:

- task: CmdLine@2
  inputs:
    script:

Ora dobbiamo creare un file con la licenza dell'analizzatore. Qui PVSNAME e PVSKEY sono i nomi delle variabili che specifichiamo nelle impostazioni. Memorizzeranno il login di PVS-Studio e la chiave di licenza. Per impostarne i valori, apri il menu Variabili->Nuova variabile . Quindi crea le variabili:PVSNAME - per il login, e PVSKEY- per la chiave dell'analizzatore. Non dimenticare di spuntare Mantieni segreto questo valore per PVSKEY . Il codice di comando:

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

Crea il progetto usando il file bat dal repository.

call build.bat

Successivamente, crea il repository per i file con i risultati dell'analizzatore:

 call mkdir PVSTestResults

Quindi, esegui l'analisi del progetto.

call "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" 
–t .\src\chocolatey.sln –o .\PVSTestResults\Choco.plog

Converti il ​​rapporto nel formato html tramite l'utilità PlogConverter:

call "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" 
–t html –o \PVSTestResults\ .\PVSTestResults\Choco.plog

Ora devi creare un'attività in modo da poter scaricare il rapporto.

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

Il file di configurazione completo ha il seguente aspetto:

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-Studio\PVS-Studio_Cmd.exe" 
      credentials –u $(PVSNAME) –n $(PVSKEY)
      call build.bat
      call mkdir PVSTestResults
      call "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" 
      –t .\src\chocolatey.sln –o .\PVSTestResults\Choco.plog
      call "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" 
      –t html –o .\PVSTestResults\ .\PVSTestResults\Choco.plog

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

Fai clic su Salva->Salva->Esegui per eseguire l'attività. Vai alla scheda attività e scarica il rapporto.

Il progetto Chocolatey contiene solo 37615 righe di codice C#. Consideriamo alcuni degli errori rilevati.

Risultati dell'analisi

Avviso N1

Avviso analizzatore:V3005 La variabile 'Provider' è assegnata a se stessa. CrytpoHashProviderSpecs.cs 38

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

L'analizzatore ha rilevato l'assegnazione di variabili a se stesso, il che non ha senso. Molto probabilmente, un'altra variabile avrebbe dovuto essere qui invece di una di queste. Oppure è un errore di battitura e il compito extra può essere semplicemente rimosso.

Avviso N2

Avviso analizzatore:V3093 [CWE-480] L'operatore '&' valuta entrambi gli operandi. Forse dovrebbe invece essere utilizzato un operatore '&&' di cortocircuito. Piattaforma.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;
  }
}

La differenza tra & e && operatori è che se la parte sinistra dell'espressione è falsa , la parte destra verrà comunque valutata se viene utilizzato &, il che, in questo caso, implica chiamate non necessarie di system.directory_exists metodo.

Nel frammento considerato, è un piccolo difetto. Sì, questa condizione può essere ottimizzata sostituendo l'operatore &con &&, ma da un punto di vista pratico non influisce su nulla. Tuttavia, in altri casi, la confusione tra &e &&può causare seri problemi, quando la parte destra dell'espressione gestisce valori errati/non validi. Ad esempio, ecco il caso della nostra raccolta di errori rilevati dalla diagnostica V3093:

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

Anche se il k l'indice è errato, verrà utilizzato per accedere all'elemento dell'array. Di conseguenza, IndexOutOfRangeException sarà generato.

Avvertenze N3, N4

Avviso dell'analizzatore:V3022 [CWE-571] L'espressione 'shortPrompt' è sempre vera. InteractivePrompt.cs 101

Avviso dell'analizzatore:V3022 [CWE-571] L'espressione 'shortPrompt' è sempre vera. 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
    ....
  }
  ....
}

In questo caso, la logica dell'operatore ternario è strana. Diamo un'occhiata sotto il cofano:se la condizione che ho segnato con il numero 1 è soddisfatta, si passa alla condizione 2, che è sempre vera , il che significa che la riga 3 verrà eseguita. Se la condizione numero 1 è falsa, salteremo alla riga, contrassegnata come 4; anche la sua condizione è sempre vera , il che significa che la riga 5 verrà eseguita. Pertanto, le condizioni contrassegnate con il commento 0 non verranno mai eseguite, il che potrebbe non essere la logica desiderata dal programmatore.

Avviso N5

Avviso dell'analizzatore:V3123 [CWE-783] Forse l'operatore '?:' funziona in modo diverso da quello previsto. La sua priorità è inferiore alla priorità di altri operatori nelle sue condizioni. Opzioni.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);
  }
}

La diagnostica è stata attivata per la riga seguente:

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

Dal momento che il j è inizializzata con 0 poche righe prima, l'operatore ternario restituirà false . A causa di questa condizione, il corpo del ciclo verrà eseguito solo una volta. Mi sembra che questo pezzo di codice non funzioni nel modo previsto dal programmatore.

Avviso N6

Avviso dell'analizzatore:V3022 [CWE-571] L'espressione 'installedPackageVersions.Count !=1' è sempre vera. 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);
    }
    ....
  }
  ....
}

La condizione nidificata installedPackageVersions.Count !=1 , che è sempre vero , è piuttosto dubbio. Spesso un tale avviso indica un errore logico nel codice o solo un controllo ridondante.

Avviso N7

Avviso dell'analizzatore:V3001 Sono presenti sottoespressioni identiche 'commandArguments.contains("-apikey")' a sinistra ea destra di '||' operatore. 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");
}

Il programmatore che ha scritto questa sezione del codice ha copiato le ultime due righe e si è dimenticato di modificarle. Per questo motivo, gli utenti di Chocolatey non sono stati in grado di applicare l'apikey parametro in un paio di altri modi. Come per il parametro sopra, posso suggerire le seguenti opzioni:

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

Gli errori di copia-incolla hanno una grande possibilità di comparire prima o poi in qualsiasi progetto con un'ampia base di codice sorgente e uno dei mezzi migliori per combatterli è l'analisi statica.

PS E come sempre, questo errore tende a comparire alla fine della condizione multilinea :). Vedi il post "Effetto ultima riga".

Avviso N8

Avviso dell'analizzatore:V3095 [CWE-476] L'oggetto 'installedPackage' è stato utilizzato prima che fosse verificato rispetto a null. Righe di controllo: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)
  {
    ....
  }
  ....
}

Un classico bug:prima il installedPackage oggetto viene utilizzato e quindi verificato per null . Questa diagnostica ci parla di uno dei problemi nel programma:o installedPackage non è mai nullo (che è discutibile) e il controllo è ridondante, oppure potremmo potenzialmente ottenere un errore grave nel codice:un tentativo di accedere a un riferimento nullo.

Conclusione

Quindi abbiamo fatto un altro piccolo passo:ora utilizzare PVS-Studio è diventato ancora più semplice e conveniente. Vorrei anche notare che Chocolatey è un ottimo gestore di pacchetti con un basso numero di errori di codice che avrebbero potuto essere ancora più piccoli se controllati da PVS-Studio.

Vi invito gentilmente a scaricare e provare PVS-Studio. L'uso regolare dell'analizzatore statico migliorerà la qualità e l'affidabilità del codice che scrivi e aiuterà a prevenire molte vulnerabilità zero-day.

PS

Prima di pubblicare l'articolo, ne abbiamo inviato uno agli sviluppatori di Chocolatey che erano abbastanza d'accordo. Non abbiamo trovato alcun bug critico lì, ma gli è piaciuto un errore relativo a "api-key", ad esempio.