PVS-Studio ahora está en Chocolatey: comprobando Chocolatey desde Azure DevOps

Figura 4

Continuamos haciendo que usar PVS-Studio sea más conveniente. Nuestro analizador ahora está disponible en Chocolatey, el administrador de paquetes para Windows. Creemos que esto facilitará la implementación de PVS-Studio, en particular, en los servicios en la nube. Para no llegar lejos, verifique el código fuente del mismo Chocolatey. El sistema CI será Azure DevOps.

Aquí hay una lista de nuestros otros artículos sobre integración en la nube:


Le aconsejo que preste atención al primer artículo sobre la integración con Azure DevOps, ya que en este caso se omiten algunos puntos para no duplicarlos.

Entonces, los héroes de este artículo:

PVS-Studio es una herramienta de análisis de código estático diseñada para detectar errores y vulnerabilidades potenciales en programas escritos en C, C ++, C # y Java. Funciona en sistemas de 64 bits en Windows, Linux y macOS, y puede analizar código diseñado para plataformas ARM integradas y de 32 bits. Si es la primera vez que prueba el análisis de código estático para verificar sus proyectos, le recomendamos que lea el artículo sobre cómo ver rápidamente las advertencias PVS-Studio más interesantes y evaluar las capacidades de esta herramienta.

Azure DevOps- Un conjunto de servicios en la nube, que cubren conjuntamente todo el proceso de desarrollo. La plataforma incluye herramientas como Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos, Azure Test Plans, que aceleran el proceso de creación de software y mejoran su calidad.

Chocolatey es un administrador de paquetes de código abierto para Windows. El objetivo del proyecto es automatizar todo el ciclo de vida del software desde la instalación hasta la actualización y desinstalación en los sistemas operativos Windows.

Sobre el uso de Chocolatey


Puede ver cómo instalar el administrador de paquetes mediante este enlace . La documentación completa sobre la instalación del analizador está disponible en el enlace en la sección "Instalación utilizando el administrador de paquetes de Chocolatey". Repetiré brevemente algunos puntos a partir de ahí.

Comando para instalar la última versión del analizador:

choco install pvs-studio

El comando para instalar una versión específica del paquete PVS-Studio:

choco install pvs-studio --version=7.05.35617.2075

De forma predeterminada, solo se instala el núcleo del analizador: el componente Core. Todos los demás indicadores (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) se pueden pasar usando --package-parameter.

Un ejemplo de un comando que instalará el analizador con un complemento para Visual Studio 2019:

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

Ahora veamos un ejemplo de uso conveniente del analizador en Azure DevOps.

Personalización


Le recuerdo que hay un artículo separado sobre temas como registrar una cuenta, crear una tubería de compilación y sincronizar una cuenta con un proyecto en el repositorio en GitHub . Nuestra configuración comenzará de inmediato escribiendo un archivo de configuración.

Primero, configure el disparador para disparar, lo que indica que solo ejecutamos cambios en la rama maestra :

trigger:
- master

A continuación, debemos elegir una máquina virtual. Por el momento, será un agente alojado por Microsoft con Windows Server 2019 y Visual Studio 2019:

pool:
  vmImage: 'windows-latest'

Pasemos al cuerpo del archivo de configuración (bloque de pasos ). A pesar de que el software arbitrario no se puede instalar en una máquina virtual, no agregué un contenedor Docker. Podemos agregar Chocolatey como una extensión para Azure DevOps. Para hacer esto, haga clic en el enlace . Haz clic en Obtener gratis . Además, si ya está autorizado, simplemente seleccione su cuenta, y si no, haga lo mismo después de la autorización.

Figura 2


Aquí debe elegir dónde agregaremos la extensión y hacer clic en el botón Instalar .

Figura 5


Después de una instalación exitosa, haga clic en Continuar a la organización :

Figura 9


Ahora puede ver la plantilla para la tarea Chocolatey en la ventana de tareas al editar el archivo de configuración azure-pipelines.yml:

Figura 6


Haga clic en Chocolatey y vea una lista de campos:

Figura 7


Aquí necesitamos seleccionar instalar en el campo de comando. En Nuspec File Name, especifique el nombre del paquete deseado: pvs-studio. Si no especifica una versión, se instalará esta última, lo que nos conviene por completo. Haga clic en el botón Agregar y vea la tarea formada en el archivo de configuración.

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

A continuación, pasemos a la parte principal de nuestro archivo:

- task: CmdLine@2
  inputs:
    script: 

Ahora necesitamos crear un archivo con la licencia del analizador. Aquí PVSNAME y PVSKEY son los nombres de las variables cuyos valores especificamos en la configuración. Almacenarán la clave de inicio de sesión y licencia de PVS-Studio. Para establecer sus valores, abra el menú Variables-> Nueva variable . Cree las variables PVSNAME para el inicio de sesión y PVSKEY para la clave del analizador. Recuerde marcar la casilla de verificación Mantener este valor secreto para PVSKEY . Código de equipo:

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

Construyamos el proyecto usando el archivo bat que se encuentra en el repositorio:

all build.bat

Cree una carpeta donde se ubicarán los archivos con los resultados del analizador:

all mkdir PVSTestResults

Ejecute el análisis del proyecto:

all "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" 
–t .\src\chocolatey.sln –o .\PVSTestResults\Choco.plog 

Convertimos nuestro informe a formato html usando la utilidad Plogonverter:

all "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" 
–t html –o \PVSTestResults\ .\PVSTestResults\Choco.plog

Ahora debe crear una tarea para poder cargar el informe.

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

El archivo de configuración completo se ve así:

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()

Haga clic en Guardar-> Guardar-> Ejecutar para comenzar la tarea. Descargue el informe yendo a la pestaña de tareas.

Figura 8



El proyecto Chocolatey contiene un total de 37,615 líneas de código C #. Considere algunos de los errores encontrados.

Resultados de validación


Advertencia N1

Analizador Advertencia: V3005 La variable 'Proveedor' se asigna a sí misma. CrytpoHashProviderSpecs.cs 38

public abstract class CrytpoHashProviderSpecsBase : TinySpec
{
  ....
  protected CryptoHashProvider Provider;
  ....
  public override void Context()
  {
    Provider = Provider = new CryptoHashProvider(FileSystem.Object);
  }
}

El analizador encontró la asignación de una variable a sí mismo, lo que no tiene sentido. Lo más probable, en lugar de una de estas variables, debería ser alguna otra. Bueno, o esto es un error tipográfico, y la asignación innecesaria simplemente se puede eliminar.

Advertencia Advertencia del

analizador N2 : V3093 [CWE-480] El operador '&' evalúa ambos operandos. Quizás debería utilizarse un operador de cortocircuito '&&'. 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;
  }
}

La diferencia entre el operador & y el operador && es que si el lado izquierdo de la expresión es falso , el lado derecho aún se calculará, lo que en este caso implica llamadas adicionales al método system.directory_exists .

En este fragmento, este es un defecto menor. Sí, esta condición puede optimizarse reemplazando el operador & con el operador &&, pero desde un punto de vista práctico, no afecta nada. Sin embargo, en otros casos, la confusión entre & y && puede causar serios problemas cuando el lado derecho de la expresión funcionará con valores incorrectos / inválidos. Por ejemplo, en nuestra colección de errores detectados por el diagnóstico V3093 , existe un caso así:

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

Incluso si el índice k es incorrecto, se utilizará para acceder al elemento de matriz. Como resultado, se lanzará una IndexOutOfRangeException .

Advertencias N3, N4

Advertencia del analizador: V3022 [CWE-571] La expresión 'shortPrompt' siempre es verdadera. InteractivePrompt.cs 101
Advertencia del analizador: V3022 [CWE-571] La expresión 'shortPrompt' siempre es verdadera. 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
    ....
  }
  ....
}

En este caso, hay una lógica extraña del operador ternario. Echemos un vistazo más de cerca: si se cumple la condición marcada por el número 1, pasamos a la condición 2, que siempre es verdadera , lo que significa que se cumple la línea 3. Si la condición 1 resulta ser falsa, entonces vamos a la línea marcada por el número 4, la condición en la que también está siempre es cierto , lo que significa que se ejecuta la línea 5. Por lo tanto, las condiciones marcadas con el comentario 0 nunca se cumplirán, lo que puede no ser exactamente la lógica del trabajo que el programador esperaba.

Advertencia Advertencia del

analizador N5 : V3123[CWE-783] Quizás el operador '?:' Funciona de una manera diferente a la esperada. Su prioridad es inferior a la prioridad de otros operadores en su condición. 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);
  }
}

El diagnóstico funcionó en una línea:

while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false)

Como la variable j se inicializa a cero varias líneas arriba, el operador ternario devolverá falso . Debido a esta condición, el cuerpo del bucle se ejecutará solo una vez. Me parece que este código no funciona en absoluto como el programador pretendía.

Advertencia Advertencia del

analizador N6 : V3022 [CWE-571] La expresión 'installedPackageVersions.Count! = 1' siempre es verdadera. 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);
    }
    ....
  }
  ....
}

Aquí hay una extraña condición anidada: installedPackageVersions.Count! = 1 , que siempre será cierto . A menudo, dicha advertencia indica un error lógico en el código, y en otros casos, simplemente una verificación redundante.

Advertencia N7

Analyzer Advertencia: V3001 Hay subexpresiones idénticas 'commandArguments.contains ("- apikey")' a la izquierda y a la derecha de '||' 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");
}

El programador que escribió este código copió las dos últimas líneas y olvidó editarlas. Debido a esto, los usuarios de Chocolatey han perdido la capacidad de aplicar el parámetro apikey de un par de formas más. Similar a los parámetros anteriores, puedo ofrecer tales opciones:

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

Los errores de copiar y pegar tienen una gran posibilidad, tarde o temprano, de aparecer en cualquier proyecto con mucho código fuente, y una de las mejores formas de lidiar con ellos es el análisis estático.

PD Y, como siempre, este error tiende a aparecer al final de una condición de varias líneas :). Consulte la publicación " El efecto de la última línea ".

Advertencia Advertencia del

analizador N8 : V3095 [CWE-476] El objeto ' paquete instalado' se usó antes de que se verificara como nulo. Líneas de verificación: 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)
  {
    ....
  }
  ....
}

Error clásico: primero, se utiliza el objeto de paquete instalado y luego se verifica si es nulo . Este diagnóstico nos dice uno de dos problemas en el programa: o bien InstallPackage nunca es nulo , lo cual es dudoso, y luego la verificación es redundante, o potencialmente podemos obtener un error grave en el código: un intento de acceso a través de un enlace nulo.

Conclusión


Así que dimos otro pequeño paso: ahora usar PVS-Studio se ha vuelto aún más fácil y más conveniente. También quiero decir que Chocolatey es un buen administrador de paquetes con una pequeña cantidad de errores en el código, lo que podría ser aún menor cuando se usa PVS-Studio.

Te invitamos a descargar y probar PVS-Studio. El uso regular de un analizador estático mejorará la calidad y confiabilidad del código desarrollado por su equipo y ayudará a prevenir muchas vulnerabilidades de día cero .

PD


Antes de la publicación, enviamos el artículo a los desarrolladores de Chocolatey, y lo aceptaron bien. No encontramos nada crítico, pero, por ejemplo, les gustó el error que encontramos relacionado con la clave API.



Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Vladislav Stolyarov. PVS-Studio ahora está en Chocolatey: Verificación de Chocolatey en Azure DevOps .

All Articles