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

PVS-Studio 现在位于 Chocolatey 中:从 Azure DevOps 下检查 Chocolatey
我们不断地让 PVS-Studio 的使用变得更加方便。 我们的分析器现在可以在 Chocolatey(Windows 的包管理器)中使用。 我们相信这将促进PVS-Studio的部署,特别是在云服务中。 为了不走得太远,让我们检查一下同一个 Chocolatey 的源代码。 Azure DevOps 将充当 CI 系统。

以下是我们关于与云系统集成主题的其他文章的列表:

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

那么,本文的主角:

PVS工作室 是一种静态代码分析工具,旨在识别用 C、C++、C# 和 Java 编写的程序中的错误和潜在漏洞。 在 64 位 Windows、Linux 和 macOS 系统上运行,可以分析为 32 位、64 位和嵌入式 ARM 平台设计的代码。 如果这是您第一次尝试静态代码分析来检查您的项目,我们建议您熟悉一下 一篇文章 关于如何快速查看最有趣的 PVS-Studio 警告并评估该工具的功能。

Azure开发运营 ——一套共同覆盖整个开发流程的云服务。 该平台包括Azure Pipelines、Azure Boards、Azure Artifacts、Azure Repos、Azure Test Plans等工具,可让您加快软件创建过程并提高其质量。

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

关于使用 Chocolatey

您可以在此处查看如何安装包管理器本身 链接。 有关安装分析仪的完整文档,请访问: 链接 请参阅使用 Chocolatey 包管理器进行安装部分。 我将简要重复其中的一些要点。

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

choco install pvs-studio

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

choco install pvs-studio --version=7.05.35617.2075

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

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

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

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

调整

让我提醒您,有一个单独的部分介绍了注册帐户、创建构建管道以及将您的帐户与 GitHub 存储库中的项目同步等问题。 文章。 我们的设置将立即开始编写配置文件。

首先,让我们设置一个启动触发器,表明我们仅针对以下内容的更改启动 分支:

trigger:
- master

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

pool:
  vmImage: 'windows-latest'

让我们继续讨论配置文件的主体(块 步骤)。 尽管你不能在虚拟机中安装任意软件,但我没有添加 Docker 容器。 我们可以添加 Chocolatey 作为 Azure DevOps 的扩展。 为此,让我们转到 链接。 点击 免费获取。 接下来,如果您已经获得授权,则只需选择您的帐户即可,如果没有,则授权后执行相同的操作。

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

在这里您需要选择我们将添加扩展程序的位置,然后单击按钮 Install 安装.

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

安装成功后,点击 继续组织:

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

您现在可以在窗口中看到 Chocolatey 任务的模板 任务 编辑配置文件时 天蓝色管道.yml:

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

单击 Chocolatey 并查看字段列表:

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

这里我们需要选择 安装 与球队一起在球场上。 在 Nuspec 文件名 指示所需包的名称 - pvs-studio。 如果不指定版本,则会安装最新版本,这完全适合我们。 让我们按下按钮 我们将在配置文件中看到生成的任务。

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

接下来,让我们继续讨论文件的主要部分:

- task: CmdLine@2
  inputs:
    script: 

现在我们需要使用分析器许可证创建一个文件。 这里 PVS名称 и PVSKEY – 我们在设置中指定其值的变量名称。 他们将存储 PVS-Studio 登录名和许可证密钥。 要设置它们的值,请打开菜单 变量->新变量。 让我们创建变量 PVS名称 用于登录和 PVSKEY 为分析仪键。 不要忘记选中该框 将此值保密 PVSKEY。 命令代码:

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

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

сall build.bat

让我们创建一个文件夹,用于存储包含分析器结果的文件:

сall mkdir PVSTestResults

让我们开始分析该项目:

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

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

сall "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
–t html –o PVSTestResults .PVSTestResultsChoco.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-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()

我们点击一​​下 保存->保存->运行 运行任务。 让我们转到任务选项卡来下载报告。

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

Chocolatey 项目仅包含 37615 行 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] '&' 运算符计算两个操作数。 也许应该使用短路“&&”运算符。 平台.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,那么右侧仍然会被计算,在这种情况下这意味着不必要的方法调用 系统目录存在.

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

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

即使指数 k 不正确,它将用于访问数组元素。 结果会抛出异常 索引超出范围异常.

警告 N3、N4

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

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

警告 N5

分析仪警告: V3123 [CWE-783] 也许“?:”运算符的工作方式与预期不同。 其优先级低于该条件下其他运营商的优先级。 选项.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

分析仪警告: 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);
    }
    ....
  }
  ....
}

这里有一个奇怪的嵌套条件: 安装的PackageVersions.Count!= 1这将永远是 true。 通常,此类警告表示代码中存在逻辑错误,而在其他情况下,它仅表示冗余检查。

警告 N7

分析仪警告: 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 用户无法应用该参数 密钥 还有几种方法。 与上面的参数类似,我可以提供以下选项:

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

在任何具有大量源代码的项目中,复制粘贴错误迟早都会出现,而解决这些错误的最佳工具之一就是静态分析。

PS 与往常一样,此错误往往出现在多行条件的末尾:)。 参见出版物“最后一行效果".

警告 N8

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

经典错误:对象优先 已安装的包 使用然后检查 。 此诊断告诉我们程序中的两个问题之一: 已安装的包 永远不平等 ,这是值得怀疑的,然后检查是多余的,或者我们可能会在代码中遇到严重错误 - 尝试访问空引用。

结论

所以我们又迈出了一小步 - 现在使用 PVS-Studio 变得更加容易和方便。 我还想说,Chocolatey 是一个很好的包管理器,代码中的错误很少,使用 PVS-Studio 时错误可能会更少。

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

PS

在发表之前,我们将这篇文章发送给了 Chocolatey 开发人员,他们收到了很好的反馈。 我们没有发现任何关键的东西,但例如,他们喜欢我们发现的与“api-key”密钥相关的错误。

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

如果您想与英语读者分享这篇文章,请使用翻译链接:Vladislav Stolyarov。 PVS-Studio 现已加入 Chocolatey:在 Azure DevOps 下检查 Chocolatey.

来源: habr.com

添加评论