
Continuamos a tornar o uso do PVS-Studio mais conveniente. Agora nosso analisador está disponível no Chocolatey, um gerenciador de pacotes para Windows. Acreditamos que isso facilitará a implantação do PVS-Studio, especialmente em serviços em nuvem. Para não nos afastarmos muito, vamos verificar o código-fonte do próprio Chocolatey. O Azure DevOps será o sistema de CI.
Aqui está uma lista de nossos outros artigos sobre integração com sistemas em nuvem:
Recomendo prestar atenção ao primeiro artigo sobre integração com o Azure DevOps, pois alguns pontos foram omitidos neste caso para não duplicar informações.
Então, os heróis deste artigo são:
— uma ferramenta de análise estática de código, destinada a identificar erros e vulnerabilidades potenciais em programas escritos nas linguagens C, C++, C# e Java. Funciona em sistemas de 64 bits no Windows, Linux e macOS, e pode analisar código destinado a plataformas ARM de 32 bits, 64 bits e embarcadas. Se você estiver tentando análise de código estática pela primeira vez em seus projetos, recomendamos ler sobre como visualizar rapidamente os avisos mais interessantes do PVS-Studio e avaliar as funcionalidades dessa ferramenta. — um conjunto de serviços em nuvem que cobre todo o processo de desenvolvimento. Esta plataforma inclui ferramentas como Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos e Azure Test Plans, que permitem acelerar o processo de criação de software e melhorar sua qualidade.
– um gerenciador de pacotes de código aberto para Windows. O objetivo do projeto é automatizar todo o ciclo de vida do software, desde a instalação até a atualização e remoção em sistemas operacionais Windows.
Sobre o uso do Chocolatey
Você pode ver como instalar o próprio gerenciador de pacotes neste
. A documentação completa sobre a instalação do analisador está disponível na seção "Instalação usando o gerenciador de pacotes Chocolatey". Resumindo, vou repetir alguns pontos de lá. Comando para instalar a versão mais recente do analisador: choco install pvs-studio
Comando para instalar uma versão específica do pacote PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Команда установки конкретной версии пакета PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Por padrão, apenas o núcleo do analisador é instalado — o componente Core. Todas as outras flags (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) podem ser passadas usando —package-parameters.
Exemplo de comando que instalará o analisador com o plugin para Visual Studio 2019:
choco install pvs-studio --package-parameters="'/MSVS2019'"Agora vamos olhar para um exemplo prático de uso do analisador no Azure DevOps.
Configuração
Lembro que sobre tópicos como registro de conta, criação de Build Pipeline e sincronização da conta com o projeto no repositório no GitHub, existe um separado . Nossa configuração começará imediatamente com a escrita do arquivo de configuração.
Primeiro, configuraremos o trigger, especificando que iniciamos apenas para mudanças em master branch:
trigger:
- masterA seguir, precisamos escolher a máquina virtual. Neste momento, será um agente hospedado pela Microsoft com Windows Server 2019 e Visual Studio 2019:
pool:
vmImage: 'windows-latest'Vamos para o corpo do arquivo de configuração (bloco steps). Apesar de que não é possível instalar software arbitrário na máquina virtual, não adicionei um container Docker. Podemos adicionar Chocolatey como uma extensão para Azure DevOps. Para isso, vamos em . Clique em Get it free. Em seguida, se você já estiver autenticado, basta escolher sua conta; caso contrário, faça o mesmo após o login.

Aqui precisamos escolher onde vamos adicionar a extensão e clicar no botão Instalar.

Após a instalação bem-sucedida, clique em Proceed to organization:

Agora é possível ver o modelo para a tarefa Chocolatey na janela tasks ao editar o arquivo de configuração azure-pipelines.yml:

Clique em Chocolatey e veja a lista de campos:

Aqui precisamos escolher install no campo de comandos. Em Nuspec File Name especificaremos o nome do pacote desejado – pvs-studio. Se não for especificada a versão, será instalada a mais recente, o que nos atende completamente. Clique no botão add e veremos a tarefa gerada 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 indicaremos nas configurações. Eles armazenarão o login e a chave de licença do PVS-Studio. Para definir seus valores, abriremos o menu Variables->New variable. Vamos criar as variáveis PVSNAME para o login e PVSKEY para a chave do analisador. Não se esqueça de marcar a caixa Mantenha este valor em segredo para PVSKEY. Código do comando:
chame "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" credenciais
–u $(PVSNAME) –n $(PVSKEY)Vamos construir o projeto usando o arquivo bat que está no repositório:
chame build.batVamos criar uma pasta onde estarão os arquivos com os resultados do trabalho do analisador:
chame mkdir PVSTestResultsVamos iniciar a análise do projeto:
chame "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog Convertemos nosso relatório para o formato html usando a ferramenta PlogConverter:
chame "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o PVSTestResults .PVSTestResultsChoco.plogAgora precisamos criar uma tarefa para que possamos exportar o relatório.
- tarefa: PublishBuildArtifacts@1
entradas:
caminhoParaPublicar: PVSTestResults
nomeDoArtefato: PVSTestResults
condição: sempre()O arquivo de configuração completo é assim:
trigger:
- master
pool:
vmImage: 'windows-latest'
steps:
- tarefa: ChocolateyCommand@0
entradas:
comando: 'install'
installPackageId: 'pvs-studio'
- tarefa: CmdLine@2
entradas:
script: |
chame "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
credenciais –u $(PVSNAME) –n $(PVSKEY)
chame build.bat
chame mkdir PVSTestResults
chame "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe"
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
chame "C:Program Files (x86)PVS-StudioPlogConverter.exe"
–t html –o .PVSTestResults .PVSTestResultsChoco.plog
- tarefa: PublishBuildArtifacts@1
entradas:
caminhoParaPublicar: PVSTestResults
nomeDoArtefato: PVSTestResults
condição: sempre()Clique em Salvar->Salvar->Executar para iniciar a tarefa. Vamos exportar o relatório acessando a aba de tarefas.
O projeto Chocolatey contém apenas 37615 linhas de código C#. Vamos considerar alguns dos erros encontrados.
Resultados da verificação
Aviso N1
Advertência do analisador: A variável ‘Provider’ está atribuída a si mesma. CrytpoHashProviderSpecs.cs 38
public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
....
protected CryptoHashProvider Provider;
....
public override void Context()
{
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}O analisador detectou a atribuição de uma variável a si mesma, que não faz sentido. Provavelmente, uma das variáveis deveria ser outra. Ou é um erro de digitação e a atribuição excessiva pode ser simplesmente removida.
Aviso N2
Advertência do analisador: [CWE-480] O operador ‘&’ avalia ambos os operandos. Talvez um operador de curto-circuito ‘&&’ devesse ser usado em seu lugar. 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;
}
}A diferença entre o operador & e o operador && é que se a parte esquerda da expressão — falso, então ainda será calculada a parte direita, o que neste caso implica chamadas desnecessárias do método system.directory_exists.
No fragmento analisado, isso é uma pequena falha. Sim, essa condição pode ser otimizada, substituindo o operador & pelo operador &&, mas, do ponto de vista prático, isso não tem efeito. No entanto, em outros casos, a confusão entre & e && pode causar sérios problemas, quando a parte direita da expressão lida com valores inválidos/indesejados. Por exemplo, na nossa coleção de erros, , existe este caso:
if ((k < nct) & (s[k] != 0.0))Mesmo que o índice k seja inválido, ele será usado para acessar o elemento do array. Como resultado, uma exceção será gerada IndexOutOfRangeException.
Avisos N3, N4
Advertência do analisador: [CWE-571] A expressão ‘shortPrompt’ é sempre verdadeira. InteractivePrompt.cs 101
Advertência do analisador: [CWE-571] A expressão ‘shortPrompt’ é sempre verdadeira. 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
....
}
....
}Neste caso, há uma lógica estranha no funcionamento do operador ternário. Vamos analisar detalhadamente: se a condição marcada com o número 1 for satisfeita, então procederemos para a condição 2, que é sempre verdadeiro, portanto, a linha 3 será executada. Se a condição 1 for falsa, vamos para a linha marcada com o número 4, cuja condição também é sempre verdadeiro, portanto, a linha 5 será executada. Assim, as condições marcadas com o comentário 0 nunca serão executadas, o que pode não ser a lógica que o programador esperava.
V587
Advertência do analisador: [CWE-783] Talvez o operador ‘?:’ funcione de maneira diferente do esperado. Sua prioridade é menor que a prioridade de outros operadores na sua condição. 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);
}
}O diagnóstico acionou na linha:
while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)Como a variável j Na linha acima, a variável é inicializada como zero; o operador ternário retornará um valor. falsoDevido 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 N6
Advertência do analisador: [CWE-571] A expressão ‘installedPackageVersions.Count != 1’ é sempre verdadeira. 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);
}
....
}
....
}Aqui há uma condição aninhada estranha: installedPackageVersions.Count != 1, que sempre será verdadeiro. Frequentemente, esse aviso indica um erro lógico no código, e em outros casos, apenas uma verificação redundante.
Aviso N7
Advertência do analisador: Existem sub-expressões idênticas ‘commandArguments.contains("-apikey")’ à esquerda e à direita do operador ‘||’. 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");
}O programador que escreveu essa parte do código copiou e colou as duas últimas linhas e esqueceu de editá-las. Por causa disso, os usuários do Chocolatey perderam a capacidade de aplicar o parâmetro apikey de outras maneiras. Semelhante aos parâmetros acima, eu posso sugerir as seguintes opções:
commandArguments.contains("-apikey=");
commandArguments.contains("-api-key=");Erros de copy-paste têm uma alta probabilidade de aparecer em qualquer projeto com uma grande quantidade de código-fonte, e um dos melhores meios de combatê-los é a análise estática.
P.S. E como sempre, esse erro tende a aparecer no final de uma condição de múltiplas linhas :). Veja a publicação "".
Aviso N8
Advertência do analisador: [CWE-476] O objeto ‘installedPackage’ foi utilizado antes de ser verificado contra nulo. Verifique as linhas: 910, 917. NugetService.cs 910
public virtual ConcurrentDictionary 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: o objeto é utilizado antes de ser verificado installedPackage na null. Este diagnóstico indica um dos dois problemas no programa: ou installedPackage nunca é igual a null, o que é duvidoso, e então a verificação é redundante, ou podemos potencialmente ter um erro sério no código – uma tentativa de acesso a um ponteiro nulo.
Conclusão
Demos mais um pequeno passo – agora usar o PVS-Studio se tornou ainda mais fácil e conveniente. Também gostaria de dizer que o Chocolatey é um bom gerenciador de pacotes com poucos erros de código, que poderiam ter sido ainda menos se o PVS-Studio tivesse sido utilizado.
Convidamos e experimentar o PVS-Studio. O uso regular do analisador estático aumentará a qualidade e a confiabilidade do código desenvolvendo por sua equipe e ajudará a prevenir muitas .
P.S.
Antes da publicação, enviamos o artigo para os desenvolvedores do Chocolatey, e eles o receberam bem. Nada crítico foi encontrado, mas, por exemplo, eles gostaram do erro que encontramos relacionado à chave "api-key".
Se você deseja compartilhar este artigo com um público de língua inglesa, por favor, utilize o link para a tradução: Vladislav Stolyarov. .
Fonte: habr.com
