我们继续使使用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的扩展。为此,请单击链接。单击免费获取。此外,如果您已经被授权,只需选择您的帐户,否则,请在授权后执行同样的操作。在这里,您需要选择添加扩展的位置,然后点击安装按钮。成功安装后,单击“ 继续组织”:现在,当您编辑azure-pipelines.yml配置文件时,您可以在任务窗口中看到Chocolatey任务的模板:单击Chocolatey,然后查看字段列表:在这里,我们需要在命令字段中选择install。在“ Nuspec文件名”中,指定所需软件包的名称-pvs -studio。如果您未指定版本,则会安装后者,这完全适合我们。单击添加按钮,然后在配置文件中查看已形成的任务。steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'
接下来,让我们进入文件的主要部分:- task: CmdLine@2
inputs:
script:
现在,我们需要使用分析器许可证创建一个文件。这里的PVSNAME和PVSKEY是我们在设置中指定其值的变量的名称。他们将存储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()
单击保存- >保存->运行以启动任务。通过转到任务选项卡卸载报告。
Chocolatey项目总共包含37,615行C#代码。考虑发现的一些错误。验证结果
警告N1分析仪警告:V3005已将 'Provider'变量分配给它自己。CrytpoHashProviderSpecs.cs 38public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}
分析器发现将变量分配给自己,这没有任何意义。最有可能取代其他变量之一。好吧,或者这是一个错字,可以简单地删除不必要的作业。警告N2分析仪警告:V3093 [CWE-480]'&'运算符将评估两个操作数。也许应该使用短路的“ &&”运算符。 Platform.cs 64public 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。互动提示105public static string
prompt_for_confirmation(.... bool shortPrompt = false, ....)
{
....
if (shortPrompt)
{
var choicePrompt = choice.is_equal_to(defaultChoice)
?
shortPrompt
?
"[[{0}]{1}]".format_with(choice.Substring(0, 1).ToUpperInvariant(),
choice.Substring(1,choice.Length - 1))
:
"[{0}]".format_with(choice.ToUpperInvariant())
:
shortPrompt
?
"[{0}]{1}".format_with(choice.Substring(0,1).ToUpperInvariant(),
choice.Substring(1,choice.Length - 1))
:
choice;
....
}
....
}
在这种情况下,三元运算符有一个奇怪的逻辑。让我们仔细看看:如果满足数字1的条件,那么我们转到条件2,该条件始终为true,这意味着满足第3行;如果条件1事实为假,那么我们转到由数字4标记的行,其中条件也为始终为true,这意味着将执行第5行,因此,注释0标记的条件将永远无法满足,这可能与程序员期望的工作逻辑不完全相同。警告N5分析仪警告:V3123[CWE-783]也许'?:'运算符的工作方式与预期的不同。在其条件下,它的优先级低于其他运营商的优先级。Options.cs 1019private 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。由于这种情况,循环体将只执行一次。在我看来,这段代码根本不符合程序员的预期。警告N6Analyzer警告:V3022 [CWE-571]表达式'installedPackageVersions.Count!= 1'始终为true。NugetService.cs 1405private 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。通常,这样的警告表示代码中的逻辑错误,而在其他情况下,仅表示冗余检查。警告N7Analyzer警告:V3001在'||'的左右两侧有相同的子表达式'commandArguments.contains(“-apikey”)' 操作员。ArgumentsUtility.cs 42public 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 910public 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下检查巧克力状态。