
Continuiamo a rendere l'uso di PVS-Studio più semplice. Ora il nostro analizzatore è disponibile su Chocolatey, il gestore di pacchetti per Windows. Riteniamo che questo faciliterà il deployment di PVS-Studio, in particolare nei servizi cloud. Per non andare lontano, controlliamo il codice sorgente dello stesso Chocolatey. Come sistema CI utilizzeremo Azure DevOps.
Ecco un elenco di altri nostri articoli sull'integrazione con i sistemi cloud:
Vi consiglio di prestare attenzione al primo articolo sull'integrazione con Azure DevOps, poiché in questo caso alcuni punti sono stati omessi per evitare ripetizioni.
Quindi, i protagonisti di questo articolo sono:
— uno strumento di analisi statica del codice, progettato per individuare errori e potenziali vulnerabilità nei programmi scritti in C, C++, C# e Java. Funziona su sistemi a 64 bit su Windows, Linux e macOS, e può analizzare codice destinato a piattaforme ARM integrate, a 32 bit e a 64 bit. Se provate per la prima volta l'analisi statica del codice per controllare i vostri progetti, vi consigliamo di consultare il su come visualizzare rapidamente gli avvisi più interessanti di PVS-Studio e valutare le capacità di questo strumento.
— insieme di servizi cloud che coprono l'intero processo di sviluppo. All'interno di questa piattaforma ci sono strumenti come Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, che consentono di accelerare il processo di creazione del software e di migliorarne la qualità.
– gestore di pacchetti per Windows con codice sorgente aperto. L'obiettivo del progetto è automatizzare l'intero ciclo di vita del software, dall'installazione all'aggiornamento e alla rimozione nei sistemi operativi Windows.
Sull'uso di Chocolatey
Puoi vedere come installare il gestore di pacchetti stesso tramite questo . La documentazione completa per l'installazione dell'analizzatore è disponibile su nella sezione 'Installazione utilizzando il gestore di pacchetti Chocolatey'. Ricapitolando 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'"Vediamo ora un esempio di utilizzo pratico dell'analizzatore in Azure DevOps.
Configurazione
Ricordo che ci sono aspetti come la registrazione di un account, la creazione di un Build Pipeline e la sincronizzazione dell'account con un progetto in repository su GitHub, per i quali esiste un . La nostra configurazione inizierà direttamente dalla scrittura del file di configurazione.
Iniziamo impostando il trigger di avvio, indicando che ci attiviamo solo per modifiche in master ramo:
trigger:
- masterSuccessivamente dobbiamo scegliere una macchina virtuale. Al momento, questo 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). Sebbene non sia possibile installare software arbitrario sulla macchina virtuale, non ho aggiunto un contenitore Docker. Possiamo aggiungere Chocolatey come estensione per Azure DevOps. Per fare ciò, andiamo su . Facciamo clic su Get it free. Successivamente, se siete già autenticati, selezionate semplicemente il vostro account, altrimenti seguite la stessa procedura dopo l'autenticazione.

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

Dopo una corretta installazione, faremo clic su Proceed to organization:

Ora possiamo vedere il modello per il task Chocolatey nella finestra tasks mentre modifichiamo il file di configurazione azure-pipelines.yml:

Facciamo clic su Chocolatey e vediamo l'elenco dei campi:

Qui dobbiamo scegliere install nel campo dei comandi. In Nuspec File Name indicheremo il nome del pacchetto necessario – pvs-studio. Se non viene specificata una versione, verrà installata l'ultima, che ci va bene. Fare clic sul pulsante add e vedremo il task generato nel file di configurazione.
steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'Procediamo ora 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. Queste conterranno il login e la chiave di licenza di PVS-Studio. Per impostare i loro valori, apriamo il menu Variables->New variable. Creiamo variabili PVSNAME per il login e PVSKEY per la chiave dell'analizzatore. Non dimenticare di selezionare la casella Mantieni 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 presente nel repository:
call build.batCreiamo una cartella dove verranno memorizzati i file dei 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 rapporto in formato html utilizzando lo strumento PlogConverter:
call "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogOra dobbiamo creare un compito per poter esportare il rapporto.
- 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()Clicchiamo Salva->Salva->Esegui per avviare il compito. Esporteremo il rapporto accedendo alla scheda dei compiti.
Il progetto Chocolatey contiene solo 37615 righe di codice C#. Esaminiamo alcuni degli errori trovati.
Risultati della verifica
Warning 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 un'assegnazione della variabile a se stessa, il che non ha senso. Probabilmente, in uno di questi casi, dovrebbe esserci un'altra variabile. Oppure potrebbe essere un errore di battitura e l'assegnazione extra può essere semplicemente eliminata.
Warning N2
Avviso dell'analizzatore: [CWE-480] L'operatore ‘&’ valuta entrambi gli operandi. Forse si dovrebbe utilizzare invece un operatore di corto circuito ‘&&’. 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 tra l'operatore & e l'operatore && risiede nel fatto che se la parte sinistra dell'espressione — false, ciò nonostante verrà calcolata la parte destra, il che in questo caso implica chiamate non necessarie al metodo system.directory_exists.
Nel frammento esaminato si tratta di una piccola imprecisione. Sì, questa condizione può essere ottimizzata sostituendo l'operatore & con l'operatore &&, 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 lavora con valori non corretti/additivi. Ad esempio, nella nostra raccolta di errori, , c'è un caso come questo:
if ((k < nct) && (s[k] != 0.0))Anche se l'indice k è errato, sarà 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 presenta una strana logica di funzionamento dell'operatore ternario. Esaminiamo più da vicino: se viene soddisfatta la condizione contrassegnata dal numero 1, passeremo alla condizione 2, che è sempre true, il che significa che verrà eseguita la riga 3. Se la condizione 1 risulta falsa, passeremo alla riga contrassegnata dal numero 4, la condizione di cui è sempre true, il che significa che verrà eseguita la riga 5. Pertanto, le condizioni contrassegnate dal commento 0 non verranno mai eseguite, il che potrebbe non essere esattamente la logica che si aspettava il programmatore.
V587
Avviso dell'analizzatore: [CWE-783] Forse l'operatore ‘?:’ funziona in un modo diverso rispetto a quanto ci si aspettava. 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 diagnosi ha attivato la linea:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Poiché la variabile j Alcuni righe sopra è inizializzato a zero, l'operatore ternario restituirà un 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 come previsto dallo sviluppatore.
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 = "Tutte le versioni";
if (installedPackageVersions.Count != 1)
{
choices.Add(allVersionsChoice);
}
....
}
....
}Qui c'è una strana condizione annidata: installedPackageVersions.Count != 1, che sarà sempre true. Spesso questo avviso indica un errore logico nel codice, mentre in altri casi è semplicemente un controllo eccessivo.
Avviso N7
Avviso dell'analizzatore: Ci sono sub-espressioni 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");
}Lo sviluppatore che ha scritto questo frammento di codice ha copiato e incollato le ultime due righe e ha dimenticato di modificarle. A causa di ciò, gli utenti di Chocolatey hanno perso la possibilità di utilizzare il parametro apikey in altri modi. Analogamente ai parametri sopra, posso suggerire le seguenti opzioni:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Gli errori di copia e incolla hanno una grande probabilità di apparire, prima o poi, in qualsiasi progetto con una grande quantità di codice sorgente, e uno dei migliori strumenti per combatterli è l'analisi statica.
P.S. E come sempre, questo errore tende a comparire alla fine di una condizione multi-linea :). Vedi la pubblicazione "".
Avviso N8
Avviso dell'analizzatore: [CWE-476] L'oggetto ‘installedPackage’ è stato utilizzato prima di essere verificato contro null. Controllare 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: l'oggetto viene utilizzato prima di essere verificato installedPackage 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 avere un serio errore nel codice – un tentativo di accesso tramite un riferimento nullo.
Conclusione
Ecco che abbiamo fatto un altro piccolo passo – ora utilizzare PVS-Studio è diventato ancora più semplice e pratico. Vorrei anche dire che Chocolatey è un buon gestore di pacchetti con un numero ridotto di errori nel codice, che potrebbero essere ancora di meno con l'uso di PVS-Studio.
Ti invitiamo e provare PVS-Studio. L'uso regolare dell'analizzatore statico migliorerà 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 l'hanno accolto bene. Non abbiamo trovato nulla di critico, ma a loro, ad esempio, è piaciuto l'errore che abbiamo trovato relativo alla chiave "api-key".
Se desideri condividere questo articolo con un pubblico anglofono, ti prego di utilizzare il link alla traduzione: Vladislav Stolyarov. .
Fonte: habr.com
