PVS-Studio is now in Chocolatey: checking Chocolatey from Azure DevOps

Figure 4

We continue to make using PVS-Studio more convenient. Our analyzer is now available in Chocolatey, the package manager for Windows. We believe that this will facilitate the deployment of PVS-Studio, in particular, in cloud services. In order not to go far, check the source code of the same Chocolatey. The CI system will be Azure DevOps.

Here is a list of our other articles on cloud integration:


I advise you to pay attention to the first article about integration with Azure DevOps, since in this case some points are omitted so as not to be duplicated.

So, the heroes of this article:

PVS-Studio is a static code analysis tool designed to detect errors and potential vulnerabilities in programs written in C, C ++, C # and Java. It works on 64-bit systems on Windows, Linux, and macOS, and can analyze code designed for 32-bit, 64-bit, and embedded ARM platforms. If this is your first time trying static code analysis to verify your projects, we recommend that you read the article on how to quickly see the most interesting PVS-Studio warnings and evaluate the capabilities of this tool.

Azure DevOps- A set of cloud services, jointly covering the entire development process. The platform includes tools such as Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, which accelerate the process of creating software and improve its quality.

Chocolatey is an open source package manager for Windows. The goal of the project is to automate the entire software life cycle from installation to updating and uninstalling on Windows operating systems.

About using Chocolatey


You can see how to install the package manager by this link . Full documentation on the installation of the analyzer is available at the link in the section “Installation using the Chocolatey package manager”. I will briefly repeat some points from there.

Command to install the latest analyzer version:

choco install pvs-studio

The command to install a specific version of the PVS-Studio package:

choco install pvs-studio --version=7.05.35617.2075

By default, only the analyzer core is installed - the Core component. All other flags (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) can be passed using --package-parameters.

An example of a command that will install the analyzer with a plug-in for Visual Studio 2019:

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

Now let's look at an example of convenient use of the analyzer under Azure DevOps.

Customization


I remind you that there is a separate article about such issues as registering an account, creating a Build Pipeline, and synchronizing an account with a project in the repository on GitHub . Our setup will immediately begin by writing a configuration file.

First, configure the trigger to trigger, indicating that we only run for changes in the master branch:

trigger:
- master

Next, we need to choose a virtual machine. At the moment, it will be a Microsoft-hosted agent with Windows Server 2019 and Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Let's move on to the body of the configuration file ( steps block ). Despite the fact that arbitrary software cannot be installed in a virtual machine, I did not add a Docker container. We can add Chocolatey as an extension for Azure DevOps. To do this, click on the link . Click Get it free . Further, if you are already authorized, just select your account, and if not, then do the same after authorization.

Figure 2


Here you need to choose where we will add the extension, and click the Install button .

Figure 5


After a successful installation, click Proceed to organization :

Figure 9


Now you can see the template for the Chocolatey task in the tasks window when editing the azure-pipelines.yml configuration file :

Figure 6


Click on Chocolatey and see a list of fields:

Figure 7


Here we need to select install in the command field. In Nuspec File Name, specify the name of the desired package - pvs-studio. If you do not specify a version, the latter will be installed, which completely suits us. Click on the add button and see the formed task in the configuration file.

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

Next, let's move on to the main part of our file:

- task: CmdLine@2
  inputs:
    script: 

Now we need to create a file with the analyzer license. Here PVSNAME and PVSKEY are the names of the variables whose values ​​we specify in the settings. They will store the PVS-Studio login and license key. To set their values, open the menu Variables-> New variable . Create the PVSNAME variables for the login and PVSKEY for the analyzer key. Remember to check the Keep this value secret checkbox for PVSKEY . Team Code:

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

Let's build the project using the bat-file lying in the repository:

all build.bat

Create a folder where the files with the results of the analyzer work will lie:

all mkdir PVSTestResults

Run the project analysis:

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

We convert our report into html format using the Plogonverter utility:

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

Now you need to create a task so that you can upload the report.

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

The full configuration file looks like this:

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

Click Save-> Save-> Run to start the task. Unload the report by going to the task tab.

Figure 8



The Chocolatey project contains a total of 37,615 lines of C # code. Consider some of the errors found.

Validation Results


Warning N1

Analyzer Warning: V3005 The 'Provider' variable is assigned to itself. CrytpoHashProviderSpecs.cs 38

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

The analyzer found the assignment of a variable to itself, which does not make sense. Most likely, in the place of one of these variables should be some other. Well, or this is a typo, and unnecessary assignment can simply be deleted.

Warning N2

Analyzer Warning: V3093 [CWE-480] The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. 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;
  }
}

The difference between the & operator and the && operator is that if the left side of the expression is false , then the right side will be calculated anyway, which in this case implies extra calls to the system.directory_exists method .

In this fragment, this is a minor flaw. Yes, this condition can be optimized by replacing the & operator with the && operator, but from a practical point of view, it does not affect anything. However, in other cases, confusion between & and && can cause serious problems when the right side of the expression will work with incorrect / invalid values. For example, in our collection of errors detected by the V3093 diagnostic , there is such a case:

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

Even if the index k is incorrect, it will be used to access the array element. As a result, an IndexOutOfRangeException will be thrown .

Warnings N3, N4

Analyzer Warning: V3022 [CWE-571] Expression 'shortPrompt' is always true. InteractivePrompt.cs 101
Analyzer Warning: V3022 [CWE-571] Expression 'shortPrompt' is always true. 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 this case, there is a strange logic of the ternary operator. Let us consider in more detail: if the condition marked by the number 1 is fulfilled, then we pass to condition 2, which is always true , which means that line 3 is satisfied. If condition 1 turns out to be false, then we go to the line marked by number 4, the condition in which is also always true , which means that line 5 is executed. Thus, the conditions marked with comment 0 will never be fulfilled, which may not be exactly the logic of the work that the programmer expected.

Warning N5

Analyzer Warning: V3123[CWE-783] Perhaps the '?:' Operator works in a different way than it was expected. Its priority is lower than priority of other operators in its condition. 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 < 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);
  }
}

Diagnostics worked on a line:

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

Since the variable j is initialized to zero several lines above, the ternary operator will return false . Due to this condition, the loop body will execute only once. It seems to me that this piece of code does not work at all as the programmer intended.

Warning N6

Analyzer Warning: V3022 [CWE-571] Expression 'installedPackageVersions.Count! = 1' is always true. 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);
    }
    ....
  }
  ....
}

Here's a weird nested condition: installedPackageVersions.Count! = 1 , which will always be true . Often, such a warning indicates a logical error in the code, and in other cases, simply a redundant check.

Warning N7

Analyzer Warning: V3001 There are identical sub-expressions 'commandArguments.contains ("- apikey")' to the left and to the right of the '||' operator. 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");
}

The programmer who wrote this piece of code copied the last two lines and forgot to edit them. Because of this, Chocolatey users have lost the ability to apply the apikey parameter in a couple more ways. Similar to the parameters above, I can offer such options:

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

Copy-paste errors have a great chance sooner or later to appear in any project with a lot of source code, and one of the best ways to deal with them is static analysis.

PS And, as always, this error tends to appear at the end of a multi-line condition :). See the publication “ The Effect of the Last Line ”.

Warning N8

Analyzer Warning: V3095 [CWE-476] The 'installedPackage' object was used before it was verified against null. Check lines: 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)
  {
    ....
  }
  ....
}

Classic error: first, the installedPackage object is used, and then it is checked for null . This diagnostic tells us one of two problems in the program: either installedPackage is never null , which is doubtful, and then the check is redundant, or we can potentially get a serious error in the code - an attempt to access via a null link.

Conclusion


So we took another small step - now using PVS-Studio has become even easier and more convenient. I also want to say that Chocolatey is a good package manager with a small number of errors in the code, which could become even less when using PVS-Studio.

We invite you to download and try PVS-Studio. The regular use of a static analyzer will improve the quality and reliability of the code developed by your team and help prevent many zero-day vulnerabilities .

PS


Before publication, we sent the article to the Chocolatey developers, and they accepted it well. We did not find anything critical, but, for example, they liked the error we found related to the api-key.



If you want to share this article with an English-speaking audience, then please use the link to the translation: Vladislav Stolyarov. PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps .

All Articles