
Continuiamo a rendere l'utilizzo di PVS-Studio più comodo. Ora il nostro analizzatore è disponibile in Chocolatey, il gestore di pacchetti per Windows. Riteniamo che ciò faciliterà il deployment di PVS-Studio, in particolare nei servizi cloud. Per non andare lontano, diamo un'occhiata al codice sorgente dello stesso Chocolatey. Come sistema CI utilizzeremo Azure DevOps.
Ecco un elenco di altri nostri articoli sull'integrazione con i sistemi cloud:
Consiglio di prestare attenzione al primo articolo sull'integrazione con Azure DevOps, poiché in questo caso alcuni dettagli sono omessi per evitare duplicazioni.
Quindi, i protagonisti di questo articolo sono:
— uno strumento di analisi statica del codice, progettato per identificare errori e potenziali vulnerabilità nei programmi scritti in linguaggi C, C++, C# e Java. Funziona su sistemi a 64 bit su Windows, Linux e macOS, e può analizzare codice destinato a piattaforme ARM 32-bit, 64-bit e embedded. Se stai provando per la prima volta l'analisi statica del codice per verificare i tuoi progetti, ti consigliamo di familiarizzare con su come visualizzare rapidamente i messaggi di avviso più interessanti di PVS-Studio e valutare le funzionalità di questo strumento.
– un insieme di servizi cloud che coprono l'intero processo di sviluppo. La piattaforma include strumenti come Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, che permettono di accelerare il processo di creazione del software e migliorarne la qualità.
– un gestore pacchetti per Windows con codice sorgente aperto. L'obiettivo del progetto è automatizzare l'intero ciclo di vita del software, dalla sua installazione all'aggiornamento e alla rimozione nei sistemi operativi Windows.
L'utilizzo di Chocolatey
Puoi vedere come installare il gestore pacchetti stesso attraverso questo . La documentazione completa sull'installazione dell'analizzatore è disponibile nella sezione «Installazione utilizzando il gestore di pacchetti Chocolatey». Riassumerò brevemente alcuni punti da lì.
Il comando per installare l'ultima versione dell'analizzatore:
choco install pvs-studioIl comando per installare una versione specifica del pacchetto PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Per impostazione predefinita, viene installato solo il nucleo dell'analizzatore — il componente Core. Tutti gli altri flag (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) possono essere passati tramite —package-parameters.
Ecco un esempio di comando che installerà l'analizzatore con il plugin per Visual Studio 2019:
choco install pvs-studio --package-parameters="'/MSVS2019'"Ora diamo un'occhiata a un esempio di utilizzo pratico dell'analizzatore con Azure DevOps.
Impostazione
Ricordo che per quanto riguarda aspetti come la registrazione di un account, la creazione di un Build Pipeline e la sincronizzazione dell'account con il progetto nel repository su GitHub, ci sono informazioni separate . La nostra configurazione inizierà direttamente dalla scrittura del file di configurazione.
Iniziamo configurando il trigger di avvio, specificando che eseguiamo l'avvio solo per le modifiche in master branch:
trigger:
- masterDopo occorre selezionare la macchina virtuale. Attualmente sarà un agente Microsoft-hosted con Windows Server 2019 e Visual Studio 2019:
pool:
vmImage: 'windows-latest'Passiamo al corpo del file di configurazione (blocco steps). Anche se non è possibile installare software arbitrario sulla macchina virtuale, non ho aggiunto un contenitore Docker. Possiamo aggiungere Chocolatey come estensione per Azure DevOps. A tal fine, andiamo su . Facciamo clic su Ottienilo gratis. Poi, se sei già autenticato, scegli semplicemente il tuo account, altrimenti esegui la stessa operazione dopo l'autenticazione.

Qui dobbiamo scegliere dove aggiungere l'estensione e premere il pulsante Installa.

Dopo l'installazione riuscita, premeremo Procedi all'organizzazione:

Ora è possibile vedere il modello per il compito Chocolatey nella finestra tasks quando si modifica il file di configurazione azure-pipelines.yml:

Clicchiamo su Chocolatey e vediamo l'elenco dei campi:

Qui dobbiamo scegliere installare nel campo dei comandi. In Nome file Nuspec indicheremo il nome del pacchetto necessario – pvs-studio. Se non viene specificata una versione, verrà installata l'ultima, il che ci soddisfa completamente. Clicchiamo sul pulsante add e vedremo il compito formattato nel file di configurazione.
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'Successivamente, 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, i cui valori specifichiamo nelle impostazioni. Essi conterranno il login e la chiave di licenza di PVS-Studio. Per impostare i loro valori, apriamo il menu Variabili->Nuova variabile. Creiamo variabili PVSNAME per il login e PVSKEY per la chiave dell'analizzatore. Non dimenticare di spuntare la casella Tieni questo valore segreto per PVSKEY. Codice del comando:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)Compiliamo il progetto utilizzando il file bat nel repository:
call build.batCreeremo una cartella dove saranno salvati i file con i risultati dell'analizzatore:
call mkdir PVSTestResultsAvviamo l'analisi del progetto:
call "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog Convertiamo il nostro report in formato html utilizzando l'utility PlogConverter:
call "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogOra dobbiamo creare un'attività per poter esportare il report.
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()Il file di configurazione completo appare così:
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()Premiamo Salva->Salva->Esegui per avviare il compito. Scaricheremo il rapporto accedendo alla scheda dei compiti.
Il progetto Chocolatey contiene un totale di 37615 righe di codice C#. Analizziamo alcuni degli errori trovati.
Risultati dell'analisi
Avviso N1
Avviso dell'analizzatore: 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 che la variabile è assegnata a se stessa, il che non ha senso. Probabilmente, al posto di una di queste variabili dovrebbe esserci un'altra variabile. Oppure si tratta di un errore di battitura e l'assegnazione superflua può essere semplicemente rimossa.
Avviso N2
Avviso dell'analizzatore: [CWE-480] L'operatore ‘&’ valuta entrambi gli operandi. Forse dovrebbe essere usato un operatore di short-circuit ‘&&’ invece. 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;
}
}La differenza dell'operatore & dell'operatore && sta nel fatto che se la parte sinistra dell'espressione è false, allora sarà comunque calcolata la parte destra, il che in questo caso implica chiamate di metodo superflue system.directory_exists.
Nel frammento considerato, si tratta di un piccolo errore. Sì, questa condizione può essere ottimizzata sostituendo l'operatore & con l'operatore &&, ma, dal punto di vista pratico, ciò non influisce su nulla. Tuttavia, in altri casi, la confusione tra & e && può causare seri problemi, quando la parte destra dell'espressione opera con valori non corretti/non validi. Ad esempio, nella nostra collezione di errori, , c'è questo caso:
if ((k < nct) & (s[k] != 0.0))Anche se l'indice k non corretto, verrà utilizzato per accedere all'elemento dell'array. Di conseguenza, verrà generata un'eccezione IndexOutOfRangeException.
Avvisi N3, N4
Avviso dell'analizzatore: [CWE-571] L'espressione 'shortPrompt' è sempre vera. InteractivePrompt.cs 101
Avviso dell'analizzatore: [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 si riscontra una logica strana nell'operatore ternario. Analizziamo più nel dettaglio: se la condizione contrassegnata con il numero 1 è vera, passeremo alla condizione 2, che è sempre true, il che significa che verrà eseguita la riga 3. Se la condizione 1 è falsa, passeremo alla riga contrassegnata con il numero 4, in cui anche la condizione è sempre true, il che significa che verrà eseguita la riga 5. Pertanto, le condizioni contrassegnate con il commento 0 non verranno mai eseguite, il che potrebbe non essere la logica che il programmatore si aspettava.
Avviso N5
Avviso dell'analizzatore: [CWE-783] È possibile che l'operatore '?:' funzioni in modo diverso rispetto alle aspettative. La sua priorità è inferiore a quella di altri operatori nella sua condizione. 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);
}
}La diagnostica ha colpito la stringa:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Poiché la variabile j è inizializzata a zero alcune righe sopra, l'operatore ternario restituirà il valore false. A causa di questa condizione, il corpo del ciclo verrà eseguito solo una volta. Mi sembra che questo frammento di codice non funzioni affatto come programmato dall’autore.
Avviso N6
Avviso dell'analizzatore: [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);
}
....
}
....
}Qui c'è una strana condizione annidata: installedPackageVersions.Count != 1, che sarà sempre vera true. Spesso questo avviso indica un errore logico nel codice, mentre in altri casi è semplicemente dovuto a controlli eccessivi.
Avviso N7
Avviso dell'analizzatore: Ci sono espressioni sub-identiche ‘commandArguments.contains("-apikey")’ a sinistra e a destra dell'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 parte di codice ha semplicemente copiato e incollato le ultime due righe e si è dimenticato di modificarle. Per questo motivo, gli utenti di Chocolatey hanno perso la possibilità di usare il parametro apikey in altri modi. Analogamente ai parametri sopra, posso proporre le seguenti opzioni:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Gli errori di copia e incolla hanno un'alta probabilità di verificarsi, prima o poi, in qualsiasi progetto con una grande quantità di codice sorgente. Uno dei migliori strumenti per combatterli è l'analisi statica.
P.S. E come sempre, questo errore tende a comparire alla fine di una condizione multilinea :). Vedi il post "".
Avviso N8
Avviso dell'analizzatore: [CWE-476] L'oggetto ‘installedPackage’ è stato utilizzato prima di essere verificato contro null. Controlla le righe: 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)
{
....
}
....
}Errore classico: all'inizio l'oggetto installedPackage è usato e poi controllato per null. Questa diagnosi ci parla di uno dei due problemi nel programma: o installedPackage non è mai uguale a null, il che è dubbio, e quindi il controllo è superfluo, oppure possiamo potenzialmente incorrere in un grave errore nel codice: un tentativo di accesso tramite un riferimento nullo.
Conclusione
Ecco che abbiamo fatto un altro piccolo passo: ora usare PVS-Studio è diventato ancora più semplice e comodo. Vorrei anche dire che Chocolatey è un buon gestore di pacchetti con un numero limitato di errori nel codice, che potrebbe diminuire ulteriormente con l'uso di PVS-Studio.
Ti invitiamo e provare PVS-Studio. L'uso regolare dell'analizzatore statico aumenterà la qualità e l'affidabilità del codice sviluppato dal vostro team e aiuterà a prevenire molte .
P.S.
Prima della pubblicazione, abbiamo inviato l'articolo agli sviluppatori di Chocolatey, e loro lo hanno accolto bene. Non abbiamo trovato nulla di critico, ma a loro, ad esempio, è piaciuto l'errore che abbiamo scoperto relativo alla chiave "api-key".
Se desiderate condividere questo articolo con un pubblico anglofono, vi prego di utilizzare il link alla traduzione: Vladislav Stolyarov. .
Fonte: habr.com
