PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps
Continuamos mejorando la usabilidad de PVS-Studio. Ahora nuestro analizador está disponible en Chocolatey, un administrador de paquetes para Windows. Creemos que esto facilitará la implementación de PVS-Studio, especialmente en servicios en la nube. Para no ir muy lejos, revisemos el código fuente del mismo Chocolatey. Azure DevOps actuará como sistema CI.

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

Te recomiendo prestar atención al primer artículo sobre integración con Azure DevOps, ya que en este caso se omiten algunos puntos para evitar duplicaciones.

Así que, los protagonistas de este artículo son:

PVS-Studio — una herramienta de análisis estático de código 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 destinado a plataformas ARM de 32 bits, 64 bits y embebidas. Si es la primera vez que pruebas un análisis estático de código para verificar tus proyectos, te recomendamos revisar artículo sobre cómo ver rápidamente las advertencias más interesantes de PVS-Studio y evaluar las capacidades de esta herramienta.

Azure DevOps — un conjunto de servicios en la nube que abarca todo el proceso de desarrollo. Esta plataforma incluye herramientas como Azure Pipelines, Azure Boards, Azure Artifacts, Azure Repos y Azure Test Plans, que permiten acelerar el proceso de creación de software y mejorar su calidad.

Chocolatey – 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 sistemas operativos Windows.

Sobre el uso de Chocolatey

Puedes ver cómo instalar el propio administrador de paquetes en este el 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 Chocolatey'. A continuación, repetiré brevemente algunos puntos de allí.

El 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

Por defecto, solo se instala el núcleo del analizador: el componente Core. Todos los demás flag (Standalone, JavaCore, IDEA, MSVS2010, MSVS2012, MSVS2013, MSVS2015, MSVS2017, MSVS2019) se pueden pasar mediante —package-parameters.

Ejemplo de comando que instalará el analizador con el complemento para Visual Studio 2019:

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

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

Configuración

Recuerdo que hay momentos como el registro de la cuenta, la creación de un Build Pipeline y la sincronización de la cuenta con el proyecto en el repositorio de GitHub, que tienen un Windows. Nuestra configuración comenzará directamente con la escritura del archivo de configuración.

Para empezar, configuraremos el desencadenador de inicio, indicando que solo activamos cambios en master la rama:

trigger:
- master

A continuación, necesitamos elegir la 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 steps). A pesar de que no se puede instalar software arbitrario en la máquina virtual, no añadí un contenedor Docker. Podemos agregar Chocolatey como una extensión para Azure DevOps. Para ello, sigamos por el enlace. Hacemos clic en Get it free. Luego, si ya has iniciado sesión, simplemente selecciona tu cuenta, y si no, realiza lo mismo después de iniciar sesión.

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

Aquí debemos elegir dónde agregaremos la extensión y presionar el botón Instalar.

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

Después de la instalación exitosa, haremos clic en Proceed to organization:

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

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

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

Haremos clic en Chocolatey y veremos la lista de campos:

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

Aquí necesitamos seleccionar install en el campo de comandos. En Nuspec File Name especificaremos el nombre del paquete requerido: pvs-studio. Si no se especifica la versión, se instalará la última, lo cual nos conviene completamente. Haremos clic en el botón add y veremos la tarea generada en el archivo de configuración.

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

A continuación, pasamos 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. Almacenan el inicio de sesión y la clave de licencia de PVS-Studio. Para establecer sus valores, abramos el menú Variables->Nueva variable. Crearemos variables PVSNAME para el inicio de sesión y PVSKEY para la clave del analizador. No olvides marcar la casilla Mantén este valor en secreto para PVSKEY. Código del comando:

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

Compilaremos el proyecto utilizando el archivo bat que se encuentra en el repositorio:

llama build.bat

Crearemos una carpeta donde se almacenarán los archivos con los resultados del analizador:

llama mkdir PVSTestResults

Iniciaremos el análisis del proyecto:

llama "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
–t .srcchocolatey.sln –o .PVSTestResultsChoco.plog 

Convertiremos nuestro informe al formato html utilizando la utilidad PlogConverter:

llama "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
–t html –o PVSTestResults .PVSTestResultsChoco.plog

Ahora necesitamos crear una tarea para poder exportar el informe.

- tarea: PublishBuildArtifacts@1
  entradas:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

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

trigger:
- master

pool:
  vmImage: 'windows-latest'

steps:
- tarea: ChocolateyCommand@0
  entradas:
    command: 'install'
    installPackageId: 'pvs-studio'

- tarea: CmdLine@2
  entradas:
    script: |
      llama "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
      credentials –u $(PVSNAME) –n $(PVSKEY)
      llama build.bat
      llama mkdir PVSTestResults
      llama "C:Program Files (x86)PVS-StudioPVS-Studio_Cmd.exe" 
      –t .srcchocolatey.sln –o .PVSTestResultsChoco.plog
      llama "C:Program Files (x86)PVS-StudioPlogConverter.exe" 
      –t html –o .PVSTestResults .PVSTestResultsChoco.plog

- tarea: PublishBuildArtifacts@1
  entradas:
    pathToPublish: PVSTestResults
    artifactName: PVSTestResults
    condition: always()

Hacemos clic en Guardar->Guardar->Ejecutar para iniciar la tarea. Exportaremos el informe accediendo a la pestaña de tareas.

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

El proyecto Chocolatey contiene un total de 37615 líneas de código C#. Revisemos algunos de los errores encontrados.

Resultados de la verificación

Advertencia N1

Advertencia del analizador: V3005 La variable 'Provider' 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 detectó que una variable se asigna a sí misma, lo cual no tiene sentido. Lo más probable es que una de estas variables deba ser diferente. O podría ser un error tipográfico, y la asignación redundante se puede eliminar.

Advertencia N2

Advertencia del analizador: V3093 [CWE-480] El operador '&' evalúa ambos operandos. Quizás debería usarse en su lugar 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 && radica en que si la parte izquierda de la expresión — false, entonces aún se calculará la parte derecha, lo que en este caso implica llamadas adicionales al método system.directory_exists.

En el fragmento considerado, esto es un pequeño descuido. Sí, esta condición se puede optimizar 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 provocar problemas graves, cuando la parte derecha de la expresión opera con valores incorrectos/inválidos. Por ejemplo, en nuestra colección de errores, identificados con el diagnóstico V3093, hay este caso:

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

Incluso si el índice k es incorrecto, se utilizará para acceder al elemento del array. Como resultado, se generará una excepción 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 en el funcionamiento del operador ternario. Veamos más de cerca: si se cumple la condición marcada por mí con el número 1, pasaremos a la condición 2, que siempre true, por lo que se ejecutará la línea 3. Si la condición 1 resulta ser falsa, pasaremos a la línea marcada con el número 4, cuya condición también siempre true, por lo que se ejecutará la línea 5. Así, las condiciones marcadas con el comentario 0 nunca se ejecutarán, lo que puede no ser la lógica de operación que el programador esperaba.

Advertencia N5

Advertencia del analizador: V3123 [CWE-783] Quizás el operador '?:' funciona de una manera diferente a la esperada. Su prioridad es menor que la 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 = 0 && j != 0 ? description [j++ - 1] == '{' : false);
    ....
    return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
  }
}

El diagnóstico se activó en la línea:

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

Dado que la variable j en unas líneas más arriba se inicializa con cero, el operador ternario devolverá un valor false. Debido a esta condición, el cuerpo del ciclo solo se ejecutará una vez. Creo que este fragmento de código no funciona como el programador imaginaba.

Advertencia N6

Advertencia del analizador: 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 = "Todas las versiones";
    if (installedPackageVersions.Count != 1)
    {
      choices.Add(allVersionsChoice);
    }
    ....
  }
  ....
}

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

Advertencia N7

Advertencia del analizador: V3001 Hay subexpresiones idénticas ‘commandArguments.contains("-apikey")’ a la izquierda y a la derecha del 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ó esta sección de código copió y pegó las dos últimas líneas y olvidó editarlas. Debido a esto, los usuarios de Chocolatey perdieron la oportunidad de usar el parámetro apikey de un par de formas más. De manera similar a los parámetros anteriores, puedo ofrecer estas opciones:

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

Los errores de copiar y pegar tienen una gran probabilidad de aparecer tarde o temprano en cualquier proyecto con una gran cantidad de código fuente, y uno de los mejores medios para combatirlos es el análisis estático.

P.D. Y como siempre, este error tiende a aparecer al final de una condición de varias líneas :). Ver publicación "Efecto de la última línea".

Advertencia N8

Advertencia del analizador: V3095 [CWE-476] El objeto ‘installedPackage’ fue utilizado antes de ser verificado contra null. Revise las líneas: 910, 917. NugetService.cs 910

diccionario concurrente virtual público<string, PackageResult> obtener_obsoleto(....)
{
  ....
  var resultadoPaqueteAnclado = paquetesObsoletos.GetOrAdd(
    nombrePaquete, 
    new PackageResult(paqueteInstalado, 
                      _sistemaArchivos.combinar_rutas(
                        ApplicationParameters.UbicaciónPaquetes, 
                        paqueteInstalado.Id)));
  ....
  if (   paqueteInstalado != null
      && !string.IsNullOrWhiteSpace(paqueteInstalado.Version.VersionEspecial) 
      && !config.ComandoUpgrade.ExcluirPrelanzamiento)
  {
    ....
  }
  ....
}

Error clásico: primero se usa el objeto paqueteInstalado y luego se verifica en null. Este diagnóstico nos indica uno de dos problemas en el programa: o bien paqueteInstalado nunca es igual a null, lo cual es dudoso, por lo que la verificación es redundante, o podríamos potencialmente tener un error grave en el código: un intento de acceder a una referencia nula.

Conclusión

Aquí hemos dado un pequeño paso más: ahora es aún más fácil y cómodo usar PVS-Studio. También quiero decir que Chocolatey es un buen gestor de paquetes con pocos errores en el código, que podrían ser aún menos al usar PVS-Studio.

Te invitamos descargar a probar PVS-Studio. El uso regular del analizador estático mejorará la calidad y la fiabilidad del código que desarrolle tu equipo y ayudará a prevenir muchas vulnerabilidades de día cero.

P.D.

Antes de la publicación, enviamos el artículo a los desarrolladores de Chocolatey, y lo recibieron positivamente. No encontramos nada crítico, pero, por ejemplo, les gustó el error que encontramos relacionado con la clave "api-key".

PVS-Studio ahora en Chocolatey: verificación de Chocolatey desde Azure DevOps

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

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster