PVS-Studio现在位于Chocolatey中:从Azure DevOps检查Chocolatey

图4

我们继续使使用PVS-Studio更加方便。现在,可在Windows的软件包管理器Chocolatey中使用我们的分析仪。我们认为,这将有助于PVS-Studio的部署,尤其是在云服务中。为了不走远,请检查同一Chocolatey的源代码。CI系统将是Azure DevOps。

这是我们有关云集成的其他文章的列表:


我建议您注意有关与Azure DevOps集成的第一篇文章,因为在这种情况下,某些要点将被省略,以免重复。

因此,本文的英雄:

PVS-Studio是一种静态代码分析工具,旨在检测用C,C ++,C#和Java编写的程序中的错误和潜在漏洞。它可以在Windows,Linux和macOS上的64位系统上运行,并且可以分析为32位,64位和嵌入式ARM平台设计的代码。如果这是你第一次尝试静态代码分析,以验证您的项目,我们建议您阅读文章如何快速看到的最有趣的PVS-Studio的警告和评价这一工具的能力。

Azure开发运营-一组云服务,共同涵盖了整个开发过程。该平台包括诸如Azure Pipelines,Azure Boards,Azure Artifacts,Azure Repos,Azure测试计划之类的工具,这些工具可以加速软件创建过程并提高其质量。

Chocolatey是Windows的开源软件包管理器。该项目的目标是使在Windows操作系统上从安装到更新和卸载的整个软件生命周期自动化。

关于使用Chocolatey


您可以通过此链接查看如何安装软件包管理器有关分析仪安装的完整文档,请参见“使用Chocolatey软件包管理器进行安装”部分中的链接我将简短地重复一些观点。

安装最新分析仪版本的命令:

choco install pvs-studio

安装特定版本的PVS-Studio软件包的命令:

choco install pvs-studio --version=7.05.35617.2075

默认情况下,仅安装分析仪核心-核心组件。可以使用--package-parameters传递所有其他标志(独立,JavaCore,IDEA,MSVS2010,MSVS2012,MSVS2013,MSVS2015,MSVS2017,MSVS2019)。

将为分析器安装Visual Studio 2019插件的命令示例:

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

现在,让我们看一下在Azure DevOps下方便使用分析器的示例。

客制化


我想提醒您,有另外一篇文章介绍了诸如注册帐户,创建构建管道以及将帐户与GitHub信息库中的项目同步等问题我们的设置将立即通过写入配置文件开始。

首先,将触发器配置为触发,表明我们仅在master分支中运行更改

trigger:
- master

接下来,我们需要选择一个虚拟机。目前,它将是Microsoft托管的Windows Server 2019和Visual Studio 2019代理:

pool:
  vmImage: 'windows-latest'

让我们继续到配置文件的主体(步骤 block )。尽管无法在虚拟机中安装任意软件,但我没有添加Docker容器。我们可以将Chocolatey添加为Azure DevOps的扩展。为此,请单击链接单击免费获取此外,如果您已经被授权,只需选择您的帐户,否则,请在授权后执行同样的操作。

图2


在这里,您需要选择添加扩展的位置,然后点击安装按钮

图5


成功安装后,单击“ 继续组织”

图9


现在,当您编辑azure-pipelines.yml配置文件时,您可以在任务窗口中看到Chocolatey任务的模板

图6


单击Chocolatey,然后查看字段列表:

图7


在这里,我们需要在命令字段中选择install在“ Nuspec文件名”中,指定所需软件包的名称-pvs -studio。如果您未指定版本,则会安装后者,这完全适合我们。单击添加按钮,然后在配置文件中查看已形成的任务。

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

接下来,让我们进入文件的主要部分:

- task: CmdLine@2
  inputs:
    script: 

现在,我们需要使用分析器许可证创建一个文件。这里的PVSNAMEPVSKEY是我们在设置中指定其值的变量的名称。他们将存储PVS-Studio登录名和许可证密钥。要设置它们的值,请打开菜单Variables-> New variable创建PVSNAME变量login和PVSKEY为分析重点。记住检查保持此值秘密复选框PVSKEY团队代码:

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

让我们使用存储库中的bat文件来构建项目:

all build.bat

创建一个文件夹,其中包含分析器工作结果的文件将位于其中:

all mkdir PVSTestResults

运行项目分析:

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

我们使用Plogonverter实用程序将报告转换为html格式:

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

完整的配置文件如下所示:

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

单击保存- >保存->运行以启动任务。通过转到任务选项卡卸载报告。

图8



Chocolatey项目总共包含37,615行C#代码。考虑发现的一些错误。

验证结果


警告N1

分析仪警告:V3005已将 'Provider'变量分配给它自己。CrytpoHashProviderSpecs.cs 38

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

分析器发现将变量分配给自己,这没有任何意义。最有可能取代其他变量之一。好吧,或者这是一个错字,可以简单地删除不必要的作业。

警告N2

分析仪警告:V3093 [CWE-480]'&'运算符将评估两个操作数。也许应该使用短路的“ &&”运算符。 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;
  }
}

运算符与&&运算符 之间的区别在于,如果表达式的左侧为false,那么仍将计算右侧,在这种情况下,这意味着需要额外调用system.directory_exists方法

在此片段中,这是一个较小的缺陷。是的,可以通过将&运算符替换为&&运算符来优化此条件,但是从实际的角度来看,它不会产生任何影响。但是,在其他情况下,当表达式的右侧将使用不正确/无效值时,&和&&&之间的混淆可能会导致严重的问题。例如,在我们的V3093诊断程序检测到的错误集合中,存在以下情况:

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

即使索引k不正确,也将用于访问数组元素。结果,将抛出IndexOutOfRangeException

警告N3,N4

分析仪警告:V3022 [CWE-571]表达式'shortPrompt'始终为true。 InteractivePrompt.cs 101
分析器警告:V3022 [CWE-571]表达式'shortPrompt'始终为true。互动提示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
    ....
  }
  ....
}

在这种情况下,三元运算符有一个奇怪的逻辑。让我们仔细看看:如果满足数字1的条件,那么我们转到条件2,该条件始终为true,这意味着满足第3行;如果条件1事实为假,那么我们转到由数字4标记的行,其中条件也为始终为true,这意味着将执行第5行,因此,注释0标记的条件将永远无法满足,这可能与程序员期望的工作逻辑不完全相同。

警告N5

分析仪警告:V3123[CWE-783]也许'?:'运算符的工作方式与预期的不同。在其条件下,它的优先级低于其他运营商的优先级。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);
  }
}

诊断工作在一条线上:

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

由于变量j在上面几行初始化为零,因此三元运算符将返回false由于这种情况,循环体将只执行一次。在我看来,这段代码根本不符合程序员的预期。

警告N6

Analyzer警告:V3022 [CWE-571]表达式'installedPackageVersions.Count!= 1'始终为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);
    }
    ....
  }
  ....
}

这是一个奇怪的嵌套条件:installedPackageVersions.Count!= 1,它将始终为true通常,这样的警告表示代码中的逻辑错误,而在其他情况下,仅表示冗余检查。

警告N7

Analyzer警告:V3001在'||'的左右两侧有相同的子表达式'commandArguments.contains(“-apikey”)' 操作员。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");
}

编写这段代码的程序员复制了最后两行,却忘记了对其进行编辑。因此,Chocolatey用户已经失去了以其他几种方式应用apikey参数的能力与以上参数类似,我可以提供以下选项:

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

复制粘贴错误迟早很可能会在具有大量源代码的任何项目中出现,处理这些错误的最佳方法之一是静态分析。

PS并且,和往常一样,此错误倾向于在多行条件的结尾出现:)。参见出版物“ 最后一行的影响 ”。

警告N8

分析仪警告:V3095 [CWE-476]在对null进行验证之前,已使用'installedPackage'对象。检查行: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)
  {
    ....
  }
  ....
}

经典错误:首先,使用installedPackage对象,然后检查该对象是否null该诊断信息告诉我们程序中的两个问题之一:要么installPackage从不为null,这是可疑的,然后检查是多余的,要么我们可能在代码中遇到严重的错误-尝试通过null链接进行访问。

结论


因此,我们又迈出了小步-现在使用PVS-Studio变得更加容易和便捷。我还想说Chocolatey是一个很好的程序包管理器,代码中有少量错误,使用PVS-Studio时错误甚至更少。

我们邀请您下载并尝试PVS-Studio。定期使用静态分析器将提高团队开发的代码的质量和可靠性,并有助于防止许多零日漏洞

聚苯乙烯


在发布之前,我们将文章发送给Chocolatey开发人员,他们对此表示满意。我们没有发现任何关键问题,但是例如,他们喜欢我们发现的与api-key相关的错误。



如果您想与说英语的读者分享这篇文章,请使用以下链接:Vladislav Stolyarov。PVS-Studio现在处于巧克力状态:在Azure DevOps下检查巧克力状态

All Articles