
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:
— 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 sobre cómo ver rápidamente las advertencias más interesantes de PVS-Studio y evaluar las capacidades de esta herramienta.
— 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.
– 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 . La documentación completa sobre la instalación del analizador está disponible en 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-studioEl comando para instalar una versión específica del paquete PVS-Studio:
choco install pvs-studio --version=7.05.35617.2075Por 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 . 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:
- masterA 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 . 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.

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

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

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

Haremos clic en Chocolatey y veremos la lista de campos:

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.batCrearemos una carpeta donde se almacenarán los archivos con los resultados del analizador:
llama mkdir PVSTestResultsIniciaremos 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.plogAhora 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.
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: 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: [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, , 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: [CWE-571] La expresión 'shortPrompt' siempre es verdadera. InteractivePrompt.cs 101
Advertencia del analizador: [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: [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: [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: 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 "".
Advertencia N8
Advertencia del analizador: [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 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 .
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".
Si desea compartir este artículo con una audiencia de habla inglesa, le pido que utilice el enlace a la traducción: Vladislav Stolyarov. .
Fuente: habr.com
