PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.
Wir arbeiten weiterhin daran, die Nutzung von PVS-Studio zu vereinfachen. Jetzt ist unser Analyzer über Chocolatey, den Paketmanager für Windows, verfügbar. Wir glauben, dass dies die Bereitstellung von PVS-Studio, insbesondere in Cloud-Diensten, erleichtern wird. Um nicht weit zu gehen, prüfen wir den Quellcode von Chocolatey. Azure DevOps wird als CI-System fungieren.

Hier ist eine Liste weiterer unserer Artikel zum Thema Integration mit Cloud-Systemen:

Ich empfehle, auf den ersten Artikel zur Integration mit Azure DevOps zu achten, da in diesem Fall einige Punkte ausgelassen wurden, um Wiederholungen zu vermeiden.

Also, die Helden dieses Artikels:

PVS-Studio — ein Werkzeug zur statischen Codeanalyse, das entwickelt wurde, um Fehler und potenzielle Schwachstellen in Programmen, die in C, C++, C# und Java geschrieben sind, zu entdecken. Es funktioniert auf 64-Bit-Systemen unter Windows, Linux und macOS und kann Code analysieren, der für 32-Bit-, 64-Bit- und eingebettete ARM-Plattformen bestimmt ist. Wenn Sie zum ersten Mal statische Codeanalyse zur Überprüfung Ihrer Projekte ausprobieren möchten, empfehlen wir Ihnen, sich mit Artikel darüber, wie Sie die interessantesten Warnungen von PVS-Studio schnell ansehen und die Möglichkeiten dieses Tools bewerten können.

Azure DevOps — eine Sammlung von cloudbasierten Dienstleistungen, die gemeinsam den gesamten Entwicklungsprozess abdecken. Diese Plattform umfasst Werkzeuge wie Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos und Azure Test Plans, die den Softwareentwicklungsprozess beschleunigen und die Qualität erhöhen.

Chocolatey – ein Open-Source-Paketmanager für Windows. Das Ziel des Projekts ist es, den gesamten Lebenszyklus von Software von der Installation bis zur Aktualisierung und Deinstallation in Windows-Betriebssystemen zu automatisieren.

Zur Verwendung von Chocolatey

Sie können sehen, wie Sie den Paketmanager selbst installieren, indem Sie auf diese dem Link. Die vollständige Dokumentation zur Installation des Analyzers ist verfügbar unter dem Link im Abschnitt „Installation mit dem Paketmanager Chocolatey“. Kurz gebe ich einige Punkte von dort wieder.

Befehl zur Installation der neuesten Version des Analyzers:

choco install pvs-studio

Befehl zur Installation einer bestimmten Version des PVS-Studio-Pakets:

choco install pvs-studio --version=7.05.35617.2075

Standardmäßig wird nur der Core-Analyzer installiert - die Komponente Core. Alle anderen Flags (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) können über —package-parameters übergeben werden.

Beispielbefehl, der den Analyzer mit dem Plugin für Visual Studio 2019 installiert:

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

Schauen wir uns nun ein Beispiel für die benutzerfreundliche Verwendung des Analyzers unter Azure DevOps an.

Einstellungen

Ich erinnere daran, dass es zu Themen wie der Registrierung eines Kontos, der Erstellung von Build-Pipelines und der Synchronisation des Kontos mit einem Projekt im Repository auf GitHub separate Artikel. Unsere Einrichtung beginnt jedoch sofort mit dem Schreiben der Konfigurationsdatei.

Zunächst konfigurieren wir den Auslöser, indem wir angeben, dass wir nur bei Änderungen in master dem Branch starten:

trigger:
- master

Als nächstes müssen wir die virtuelle Maschine auswählen. Momentan wird es ein Microsoft-hosted Agent mit Windows Server 2019 und Visual Studio 2019 sein:

pool:
  vmImage: 'windows-latest'

Kommen wir zum Hauptteil der Konfigurationsdatei (Block steps). Obwohl auf der virtuellen Maschine keine beliebige Software installiert werden kann, habe ich keinen Docker-Container hinzugefügt. Wir können Chocolatey als Erweiterung für Azure DevOps hinzufügen. Dafür gehen wir zu dem Link. Klicken Sie auf Get it free. Falls Sie bereits angemeldet sind, wählen Sie einfach Ihr Konto aus; andernfalls folgen Sie dem gleichen Verfahren nach der Anmeldung.

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Hier müssen wir auswählen, wo wir die Erweiterung hinzufügen werden, und auf die Schaltfläche klicken Installieren.

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Nachdem die Installation erfolgreich abgeschlossen ist, klicken wir auf Proceed to organization:

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Nun können wir die Vorlage für die Chocolatey-Aufgabe im Fenster sehen tasks beim Bearbeiten der Konfigurationsdatei azure-pipelines.yml:

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Klicken Sie auf Chocolatey und sehen Sie die Liste der Felder:

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Hier müssen wir auswählen install im Befehlsfeld. In Nuspec File Name geben wir den Namen des benötigten Pakets an – pvs-studio. Wenn keine Version angegeben ist, wird die neueste installiert, was uns vollkommen zufriedenstellt. Klicken Sie auf die Schaltfläche add und sehen Sie die erstellte Aufgabe in der Konfigurationsdatei.

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

Kommen wir nun zum Hauptteil unserer Datei:

- task: CmdLine@2
  inputs:
    script: 

Jetzt müssen wir eine Lizenzdatei für den Analyzer erstellen. Hier PVSNAME und PVSKEY – sind die Namen der Variablen, deren Werte wir in den Einstellungen angeben. Sie speichern den Login und den Lizenzschlüssel von PVS-Studio. Um ihre Werte zu setzen, öffnen wir das Menü Variables->New variable. Lassen Sie uns die Variablen PVSNAME für den Login und PVSKEY für den Analyse-Schlüssel. Vergessen Sie nicht, das Kästchen anzukreuzen Halten Sie diesen Wert geheim für PVSKEY. Befehlscode:

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

Lassen Sie uns das Projekt mit der in dem Repository vorhandenen bat-Datei zusammenstellen:

call build.bat

Lassen Sie uns einen Ordner erstellen, in dem die Ergebnisse der Analyse gespeichert werden:

call mkdir PVSTestResults

Lassen Sie uns die Analyse des Projekts starten:

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

Konvertieren wir unseren Bericht mit dem PlogConverter in das HTML-Format:

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

Jetzt müssen wir einen Job erstellen, um den Bericht exportieren zu können.

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

Die vollständige Konfigurationsdatei sieht so aus:

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

Klicken wir Speichern->Speichern->Ausführen für die Ausführung des Jobs. Wir werden den Bericht abrufen, indem wir zum Tab Job gehen.

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Das Chocolatey-Projekt besteht aus insgesamt 37615 Zeilen C#-Code. Betrachten wir einige der gefundenen Fehler.

Ergebnisse der Überprüfung

Warnung N1

Warnung des Analysewerkzeugs: V3005 Die ‘Provider’-Variable wird sich selbst zugewiesen. CrytpoHashProviderSpecs.cs 38

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

Der Analysewerkzeug hat die Zuweisung einer Variablen zu sich selbst entdeckt, was keinen Sinn ergibt. Wahrscheinlich sollte an der Stelle einer dieser Variablen eine andere stehen. Oder es handelt sich um einen Tippfehler, und die zusätzliche Zuweisung kann einfach gelöscht werden.

Warnung N2

Warnung des Analysewerkzeugs: V3093 [CWE-480] Der ‘&’-Operator bewertet beide Operanden. Vielleicht sollte stattdessen ein Kurzschluss-‘&&’-Operator verwendet werden. 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;
  }
}

Der Unterschied zwischen dem Operator & und dem Operator && liegt darin, dass die linke Seite des Ausdrucks — false, es wird trotzdem der rechte Teil berechnet, was in diesem Fall überflüssige Methodenausführungen impliziert system.directory_exists.

Im betrachteten Fragment handelt es sich um einen kleinen Mangel. Ja, dieser Ausdruck kann optimiert werden, indem der Operator & durch den Operator && ersetzt wird, aber aus praktischer Sicht hat das keinen Einfluss. In anderen Fällen kann jedoch die Verwirrung zwischen & und && ernsthafte Probleme verursachen, wenn der rechte Teil des Ausdrucks mit inkorrekten oder unzulässigen Werten arbeitet. Zum Beispiel gibt es in unserer Sammlung von Fehlern, die mit der Diagnose V3093 aufgedeckt wurden, folgenden Fall:

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

Selbst wenn der Index k inkorrekt ist, wird er verwendet, um auf das Array-Element zuzugreifen. In der Folge wird eine Ausnahme ausgelöst IndexOutOfRangeException.

Warnungen N3, N4

Warnung des Analysewerkzeugs: V3022 [CWE-571] Ausdruck 'shortPrompt' ist immer wahr. InteractivePrompt.cs 101
Warnung des Analysewerkzeugs: V3022 [CWE-571] Ausdruck 'shortPrompt' ist immer wahr. 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 diesem Fall gibt es eine seltsame Logik in der Funktionsweise des ternären Operators. Lassen Sie uns das näher betrachten: Wenn die Bedingung, die ich mit der Zahl 1 gekennzeichnet habe, erfüllt ist, gehen wir zur Bedingung 2 über, die immer true, also wird Zeile 3 ausgeführt. Wenn jedoch Bedingung 1 falsch ist, gehen wir zu der Zeile, die mit der Zahl 4 gekennzeichnet ist, wobei die Bedingung ebenfalls immer true, also wird Zeile 5 ausgeführt. Somit werden die Bedingungen, die mit dem Kommentar 0 gekennzeichnet sind, niemals ausgeführt, was möglicherweise nicht die Logik ist, die der Programmierer erwartet hat.

Warnung N5

Warnung des Analysewerkzeugs: V3123 [CWE-783] Vielleicht funktioniert der '?:'-Operator anders als erwartet. Seine Priorität ist niedriger als die anderen Operatoren in seiner Bedingung. 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);
  }
}

Die Diagnose hat für die Zeile ausgelöst:

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

Da die Variable j Ein paar Zeilen höher wird es mit null initialisiert, der ternäre Operator gibt den Wert zurück. false. Aufgrund dieser Bedingung wird der Schleifenrumpf nur einmal ausgeführt. Ich habe das Gefühl, dass dieser Codeabschnitt ganz anders funktioniert, als der Programmierer beabsichtigt hat.

Warnung N6

Warnung des Analysewerkzeugs: V3022 [CWE-571] Ausdruck ‘installedPackageVersions.Count != 1’ ist immer wahr. NugetService.cs 1405

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

Hier ist eine seltsame geschachtelte Bedingung: installedPackageVersions.Count != 1, die immer wahr sein wird true. Solche Warnungen deuten oft auf einen logischen Fehler im Code hin, in anderen Fällen einfach auf überflüssige Überprüfungen.

Warnung N7

Warnung des Analysewerkzeugs: V3001 Es gibt identische Teil-Ausdrücke ‘commandArguments.contains("-apikey")’ links und rechts des ‘||’-Operators. 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");
}

Der Programmierer, der diesen Codeabschnitt geschrieben hat, hat die letzten beiden Zeilen kopiert und vergessen, sie zu bearbeiten. Dadurch haben die Benutzer von Chocolatey die Möglichkeit verloren, den Parameter apikey auf noch einige andere Arten zu verwenden. Analog zu den oben genannten Parametern schlage ich folgende Varianten vor:

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

Copy-Paste-Fehler treten früher oder später in jedem Projekt mit viel Code auf, und eines der besten Mittel zur Bekämpfung ist die statische Analyse.

P.S. Und wie immer neigt dieser Fehler dazu, am Ende einer mehrzeiligen Bedingung aufzutreten :). Siehe Veröffentlichung "Effekt der letzten Zeile".

Warnung N8

Warnung des Analysewerkzeugs: V3095 [CWE-476] Das ‘installedPackage’-Objekt wurde verwendet, bevor es auf null überprüft wurde. Überprüfen Sie die Zeilen: 910, 917. NugetService.cs 910

öffentliche virtuelle ConcurrentDictionary<string, PackageResult> get_outdated(....)
{
  ....
  var pinnedPackageResult = outdatedPackages.GetOrAdd(
    packageName, 
    new PackageResult(installedPackage, 
                      _fileSystem.combine_paths(
                        ApplicationParameters.PackagesLocation, 
                        installedPackage.Id)));
  ....
  wenn (   installedPackage != null
      && !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion) 
      && !config.UpgradeCommand.ExcludePrerelease)
  {
    ....
  }
  ....
}

Klassischer Fehler: Zuerst wird das Objekt installedPackage verwendet, dann wird es auf null. Diese Diagnose weist auf eines von zwei Problemen im Programm hin: entweder installedPackage niemals gleich null, was zweifelhaft ist, und die Prüfung dann überflüssig ist, oder wir könnten potenziell einen schweren Fehler im Code bekommen – den Versuch, auf eine Nullreferenz zuzugreifen.

Fazit

Hier haben wir einen weiteren kleinen Schritt gemacht – jetzt ist die Nutzung von PVS-Studio noch einfacher und bequemer geworden. Ich möchte auch sagen, dass Chocolatey ein guter Paketmanager ist, mit wenigen Fehlern im Code, die noch geringer wären, wenn PVS-Studio verwendet würde.

Wir laden ein herunterladen und PVS-Studio auszuprobieren. Die regelmäßige Verwendung des statischen Analysators wird die Qualität und Zuverlässigkeit des von Ihrem Team entwickelten Codes erhöhen und helfen, viele Zero-Day-Schwachstellen.

P.S.

Vor der Veröffentlichung haben wir den Artikel an die Entwickler von Chocolatey gesendet, und sie haben ihn gut angenommen. Kritisches wurde von uns nicht gefunden, aber ihnen hat zum Beispiel der Fehler gefallen, den wir in Bezug auf den Schlüssel „api-key“ gefunden haben.

PVS-Studio jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Vladislav Stolyarov. PVS-Studio ist jetzt in Chocolatey: Überprüfung von Chocolatey unter Azure DevOps.

Quelle: habr.com

60GB SSD 8Gb DDR4