Agora, o PVS-Studio está no Chocolatey: verificando o Chocolatey no Azure DevOps

Figura 4

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.

Figura 2


Aqui você precisa escolher onde adicionaremos a extensão e clicar no botão Instalar .

Figura 5


Após uma instalação bem-sucedida, clique em Continuar com a organização :

Figura 9


Agora você pode ver o modelo da tarefa Chocolatey na janela de tarefas ao editar o arquivo de configuração azure-pipelines.yml:

Figura 6


Clique em Chocolatey e veja uma lista de campos:

Figura 7


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.

Figura 8



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 N1

Analyzer Warning: V3005 A variável 'Provider' é 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 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 N2

Analyzer Warning: V3093 [CWE-480] O operador '&' avalia os dois operandos. Talvez um operador de curto-circuito '&&' deva ser usado. 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 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, N4

Analyzer Warning: V3022 [CWE-571] A expressão 'shortPrompt' sempre é verdadeira. InteractivePrompt.cs 101
Analisador Aviso: V3022 [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
    ....
  }
  ....
}

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 N5

Analyzer 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 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);
  }
}

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 N6

Analyzer Warning: V3022 [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 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 N7

Analyzer Aviso: V3001 Existem sub-expressões idênticas 'commandArguments.contains ("- apikey")' à esquerda e à direita da '||' 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 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 N8

Analyzer Aviso: V3095 [CWE-476] O objeto 'installedPackage' foi usado antes de ser verificado com valor nulo. Verifique as linhas: 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)
  {
    ....
  }
  ....
}

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 .

All Articles