Continuamos a tornar o uso do PVS-Studio mais conveniente. Nosso analisador está agora disponível no Chocolatey, o gerenciador de pacotes para Windows. Acreditamos que isso facilitará a implantação do PVS-Studio, em particular, nos serviços em nuvem. Para não ir muito longe, verifique o código fonte do mesmo Chocolatey. O sistema de IC será o DevOps do Azure.Aqui está uma lista de nossos outros artigos sobre integração na nuvem:Aconselho que você preste atenção no primeiro artigo sobre integração com o Azure DevOps, pois nesse caso alguns pontos são omitidos para não serem duplicados.Portanto, os heróis deste artigo:PVS-Studio é uma ferramenta de análise de código estática projetada para detectar erros e possíveis vulnerabilidades em programas escritos em C, C ++, C # e Java. Ele funciona em sistemas de 64 bits no Windows, Linux e macOS e pode analisar o código projetado para plataformas ARM de 32 bits, 64 bits e incorporadas. Se esta é a primeira vez que você tenta a análise de código estático para verificar seus projetos, recomendamos que você leia o artigo sobre como ver rapidamente os avisos mais interessantes do PVS-Studio e avaliar os recursos desta ferramenta.DevOps do Azure- Um conjunto de serviços em nuvem, cobrindo conjuntamente todo o processo de desenvolvimento. A plataforma inclui ferramentas como Pipelines do Azure, Placas do Azure, Artefatos do Azure, Repos do Azure, Planos de Teste do Azure, que aceleram o processo de criação de software e melhoram sua qualidade.Chocolatey é um gerenciador de pacotes de código aberto para Windows. O objetivo do projeto é automatizar todo o ciclo de vida do software, da instalação à atualização e desinstalação dos sistemas operacionais Windows.Sobre o uso do Chocolatey
Você pode ver como instalar o gerenciador de pacotes por este link . A documentação completa sobre a instalação do analisador está disponível no link na seção "Instalação usando o gerenciador de pacotes Chocolatey". Vou repetir brevemente alguns pontos a partir daí.Comando para instalar a versão mais recente do analisador:choco install pvs-studio
O comando para instalar uma versão específica do pacote PVS-Studio:choco install pvs-studio --version=7.05.35617.2075
Por padrão, apenas o núcleo do analisador está instalado - o componente Core. Todos os outros sinalizadores (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) podem ser transmitidos usando --package-parameters.Um exemplo de comando que instalará o analisador com um plug-in para o Visual Studio 2019:choco install pvs-studio --package-parameters="'/MSVS2019'"
Agora, vamos ver um exemplo de uso conveniente do analisador no Azure DevOps.Costumização
Lembro que há um artigo separado sobre problemas como registrar uma conta, criar um Pipeline de Compilação e sincronizar uma conta com um projeto no repositório no GitHub . Nossa instalação começará imediatamente escrevendo um arquivo de configuração.Primeiro, configure o gatilho para disparar, indicando que apenas executamos alterações na ramificação principal :trigger:
- master
Em seguida, precisamos escolher uma máquina virtual. No momento, será um agente hospedado pela Microsoft com o Windows Server 2019 e o Visual Studio 2019:pool:
vmImage: 'windows-latest'
Vamos para o corpo do arquivo de configuração (bloco de etapas ). Apesar do software arbitrário não poder ser instalado em uma máquina virtual, não adicionei um contêiner do Docker. Podemos adicionar o Chocolatey como uma extensão do DevOps do Azure. Para fazer isso, clique no link . Clique em Obtenha gratuitamente . Além disso, se você já está autorizado, basta selecionar sua conta e, se não estiver, faça o mesmo após a autorização.Aqui você precisa escolher onde adicionaremos a extensão e clicar no botão Instalar .Após uma instalação bem-sucedida, clique em Continuar com a organização :Agora você pode ver o modelo da tarefa Chocolatey na janela de tarefas ao editar o arquivo de configuração azure-pipelines.yml:Clique em Chocolatey e veja uma lista de campos:Aqui precisamos selecionar instalar no campo de comando. Em Nome do arquivo Nuspec, especifique o nome do pacote desejado - pvs-studio. Se você não especificar uma versão, a última será instalada, o que nos convém completamente. Clique no botão Adicionar e veja a tarefa formada no arquivo de configuração.steps:
- task: ChocolateyCommand@0
inputs:
command: 'install'
installPackageId: 'pvs-studio'
Em seguida, vamos para a parte principal do nosso arquivo:- task: CmdLine@2
inputs:
script:
Agora precisamos criar um arquivo com a licença do analisador. Aqui PVSNAME e PVSKEY são os nomes das variáveis cujos valores especificamos nas configurações. Eles irão armazenar o login e a chave de licença do PVS-Studio. Para definir seus valores, abra o menu Variáveis-> Nova variável . Crie as variáveis PVSNAME para o login e PVSKEY para a chave do analisador. Lembre-se de marcar a caixa de seleção Manter este valor secreto para PVSKEY . Código da equipe:all "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials
–u $(PVSNAME) –n $(PVSKEY)
Vamos construir o projeto usando o arquivo bat no repositório:all build.bat
Crie uma pasta na qual os arquivos com os resultados do analisador funcionem:all mkdir PVSTestResults
Execute a análise do projeto:all "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe"
–t .\src\chocolatey.sln –o .\PVSTestResults\Choco.plog
Convertemos nosso relatório em formato html usando o utilitário Plogonverter:all "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe"
–t html –o \PVSTestResults\ .\PVSTestResults\Choco.plog
Agora você precisa criar uma tarefa para poder fazer upload do relatório.- task: PublishBuildArtifacts@1
inputs:
pathToPublish: PVSTestResults
artifactName: PVSTestResults
condition: always()
O arquivo de configuração completo fica assim: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()
Clique em Salvar-> Salvar-> Executar para iniciar a tarefa. Descarregue o relatório indo para a guia Tarefa.
O projeto Chocolatey contém um total de 37.615 linhas de código C #. Considere alguns dos erros encontrados.Resultados da validação
Aviso N1Analyzer Warning: V3005 A variável 'Provider' é atribuída a si mesma. CrytpoHashProviderSpecs.cs 38public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}
O analisador encontrou a atribuição de uma variável para si mesma, o que não faz sentido. Muito provavelmente, no lugar de uma dessas variáveis deve haver alguma outra. Bem, ou isso é um erro de digitação, e tarefas desnecessárias podem ser simplesmente excluídas.Advertência N2Analyzer Warning: V3093 [CWE-480] O operador '&' avalia os dois operandos. Talvez um operador de curto-circuito '&&' deva ser usado. 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;
}
}
A diferença entre o operador & e o operador && é que, se o lado esquerdo da expressão for falso , o lado direito ainda será calculado, o que, nesse caso, implica chamadas adicionais para o método system.directory_exists .Nesse fragmento, essa é uma falha menor. Sim, essa condição pode ser otimizada substituindo o operador & pelo operador &&, mas, do ponto de vista prático, isso não afeta nada. No entanto, em outros casos, a confusão entre & e && pode causar sérios problemas quando o lado direito da expressão funcionar com valores incorretos / inválidos. Por exemplo, em nossa coleção de erros detectados pelo diagnóstico V3093 , existe um caso:if ((k < nct) & (s[k] != 0.0))
Mesmo se o índice k estiver incorreto, ele será usado para acessar o elemento da matriz. Como resultado, uma IndexOutOfRangeException será lançada .Avisos N3, N4Analyzer Warning: V3022 [CWE-571] A expressão 'shortPrompt' sempre é verdadeira. InteractivePrompt.cs 101Analisador Aviso: V3022 [CWE-571] A expressão 'shortPrompt' sempre é verdadeira. InteractivePrompt.cs 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;
....
}
....
}
Nesse caso, existe uma lógica estranha do operador ternário. Vamos dar uma olhada: se a condição marcada pelo número 1 for atendida, passamos à condição 2, que é sempre verdadeira , o que significa que a linha 3. é satisfeita. Se a condição 1 for falsa, passamos à linha marcada pelo número 4, a condição na qual também é Sempre verdadeiro , o que significa que a linha 5. é executada. Assim, as condições marcadas com o comentário 0 nunca serão atendidas, o que pode não ser exatamente a lógica do trabalho que o programador esperava.Aviso N5Analyzer Warning: V3123[CWE-783] Talvez o operador '?:' Funcione de maneira diferente do esperado. Sua prioridade é menor que a prioridade de outros operadores em sua condição. 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);
}
}
Os diagnósticos trabalharam em uma linha:while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)
Como a variável j é inicializada para zerar várias linhas acima, o operador ternário retornará false . Devido a essa condição, o corpo do loop será executado apenas uma vez. Parece-me que esse trecho de código não funciona como o programador pretendia.Aviso N6Analyzer Warning: V3022 [CWE-571] A expressão 'installedPackageVersions.Count! = 1' sempre é verdadeira. 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);
}
....
}
....
}
Aqui está uma condição aninhada estranha: installedPackageVersions.Count! = 1 , que sempre será verdadeira . Geralmente, esse aviso indica um erro lógico no código e, em outros casos, simplesmente uma verificação redundante.Aviso N7Analyzer Aviso: V3001 Existem sub-expressões idênticas 'commandArguments.contains ("- apikey")' à esquerda e à direita da '||' operador. 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");
}
O programador que escreveu esse trecho de código copiou as duas últimas linhas e esqueceu de editá-las. Por esse motivo , os usuários do Chocolatey perderam a capacidade de aplicar o parâmetro apikey de mais algumas maneiras. Semelhante aos parâmetros acima, posso oferecer essas opções:commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");
Os erros de copiar e colar têm uma grande chance, mais cedo ou mais tarde, de aparecer em qualquer projeto com muito código-fonte e uma das melhores maneiras de lidar com eles é a análise estática.PS E, como sempre, esse erro tende a aparecer no final de uma condição de várias linhas :). Veja a publicação “ O efeito da última linha ”.Aviso N8Analyzer Aviso: V3095 [CWE-476] O objeto 'installedPackage' foi usado antes de ser verificado com valor nulo. Verifique as linhas: 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)
{
....
}
....
}
Erro clássico: primeiro, o objeto installedPackage é usado e, em seguida, é verificado se há nulo . Esse diagnóstico nos mostra um dos dois problemas no programa: o packagePackage nunca é nulo , o que é duvidoso e a verificação é redundante, ou podemos obter um erro grave no código - uma tentativa de acessar por um link nulo.Conclusão
Então, demos outro pequeno passo - agora, o uso do PVS-Studio se tornou ainda mais fácil e conveniente. Também quero dizer que o Chocolatey é um bom gerenciador de pacotes com um pequeno número de erros no código, que podem se tornar ainda menores ao usar o PVS-Studio.Nós convidamos você a baixar e experimentar o PVS-Studio. O uso regular de um analisador estático melhorará a qualidade e a confiabilidade do código desenvolvido por sua equipe e ajudará a evitar muitas vulnerabilidades de dia zero .PS
Antes da publicação, enviamos o artigo aos desenvolvedores do Chocolatey, e eles o aceitaram bem. Não encontramos nada crítico, mas, por exemplo, eles gostaram do erro que encontramos relacionado à api-key.
Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Vladislav Stolyarov. Agora, o PVS-Studio está no Chocolatey: Verificando o Chocolatey no Azure DevOps .