Azure PowerShell:mayormente inofensivo

 C Programming >> Programación C >  >> Tags >> Azure
Azure PowerShell:mayormente inofensivo

Hola a todos. Hoy tenemos otro proyecto de Microsoft en el cheque. Por el título de este artículo, puede adivinar que esta vez los desarrolladores no nos "complaceron" con una gran cantidad de errores. Esperamos que los autores del proyecto no se sientan ofendidos por el título. Después de todo, una pequeña cantidad de errores es excelente, ¿no es así? Sin embargo, logramos encontrar algo intrigante en el código de Azure PowerShell. Sugerimos familiarizarse con las funciones de este proyecto y comprobar los errores encontrados con el analizador PVS-Studio C#.

Sobre el proyecto

Azure PowerShell es un conjunto de comandos (cmdlet) que le permite administrar los recursos de Azure directamente desde la línea de comandos de PowerShell. El objetivo principal de este conjunto es simplificar el proceso de estudio y comenzar rápidamente el desarrollo de Azure. Azure PowerShell proporciona a los administradores y desarrolladores características atractivas para crear, implementar y administrar aplicaciones de Microsoft Azure.

Azure PowerShell se desarrolla en el entorno .NET Standard, es compatible con PowerShell 5.1 para Windows y PowerShell 6.x y versiones posteriores para todas las plataformas. El código fuente de Azure PowerShell está disponible en GitHub.

Recientemente he estado recibiendo proyectos de Microsoft a menudo para un cheque. En mi opinión, la calidad de estos proyectos suele ser de primera. Aunque, por supuesto, no sin excepciones, como se describe en el artículo "WinForms:errores, Holmes". Pero esta vez todo está bien. El proyecto es grande:los archivos de código fuente 6845 .cs contienen aproximadamente 700,000 líneas, excluyendo las en blanco (no tomé en cuenta las pruebas y advertencias del tercer nivel de certeza). Se encontraron muy pocos errores para tal cantidad de código:no más de cien. Hubo bastantes casos similares, así que elegí los más interesantes para el artículo. Como de costumbre, ordené los errores por los números de advertencias de PVS-Studio.

También me topé con algunos fragmentos de código que parecían errores, pero que no se podían reconocer como definitivamente erróneos, ya que no estoy lo suficientemente familiarizado con las peculiaridades de desarrollo de PowerShell. Espero que entre los lectores haya especialistas en este tema que me ayuden. Lo describiré en detalle a continuación.

Antes de la parte de comentarios, me gustaría mencionar la estructura específica del proyecto. El código fuente de Azure PowerShell consta de más de setenta soluciones de Visual Studio. Algunas soluciones incluyen proyectos de otras. Esta estructura ralentizaba un poco el análisis (no mucho). Aún así, el cheque no causó ninguna otra dificultad. Para mayor comodidad, en el mensaje de error (entre paréntesis) especificaré el nombre de la solución donde se encontró el error.

Resultados del análisis

V3001 Hay subexpresiones idénticas 'strTimespan.Contains("M")' a la izquierda y a la derecha de '||' operador. AzureServiceBusCmdletBase.cs 187 (EventGrid)

public static TimeSpan ParseTimespan(string strTimespan)
{
  ....
  if (strTimespan.Contains("P") 
    || strTimespan.Contains("D") 
    || strTimespan.Contains("T") 
    || strTimespan.Contains("H") 
    || strTimespan.Contains("M") 
    || strTimespan.Contains("M"))
  ....
}

Un ejemplo de un error bastante obvio que solo un desarrollador puede solucionar. En este caso, no está del todo claro si nos ocupamos de la duplicación de código que no afecta a nada o si tiene que ocurrir algo más en lugar de "M" en una de las dos últimas comprobaciones.

V3001 Hay subexpresiones idénticas 'this.AggregationType !=null' a la izquierda ya la derecha del operador '&&'. GetAzureRmMetricCommand.cs 156 (supervisar)

public AggregationType? AggregationType { get; set; }
....
protected override void ProcessRecordInternal()
{
  ....
  string aggregation = (this.AggregationType != null &&
    this.AggregationType.HasValue) ?
    this.AggregationType.Value.ToString() : null;
  ....
}

Probablemente no haya ningún error aquí. Este es un ejemplo de código redundante. A veces, dicho código puede indicar la falta de conocimiento del desarrollador. El punto es que los controles this.AggregationType !=null y este.AggregationType.HasValue Son identicos. Es suficiente usar solo uno de ellos (cualquiera). Personalmente, prefiero la opción con HasValue:

string aggregation = this.AggregationType.HasValue ?
  this.AggregationType.Value.ToString() : 
  null;

V3003 Se detectó el uso del patrón 'if (A) {...} else if (A) {...}'. Hay una probabilidad de presencia de error lógico. Verifique las líneas:152, 163. GetAzureRmRecoveryServicesBackupProtectionPolicy.cs 152 (RecoveryServices)

public override void ExecuteCmdlet()
{
  ....
  if( WorkloadType == Models.WorkloadType.AzureVM )
  {
    ....
  }
  ....
  else if( WorkloadType == Models.WorkloadType.AzureFiles )
  {
    if( BackupManagementType != Models.BackupManagementType.AzureStorage )
    {
      throw new ArgumentException(
        Resources.AzureFileUnsupportedBackupManagementTypeException );
    }
    serviceClientProviderType = ServiceClientHelpers.
      GetServiceClientProviderType( Models.WorkloadType.AzureFiles );
  }
  else if( WorkloadType == Models.WorkloadType.AzureFiles )
  {
    if( BackupManagementType != Models.BackupManagementType.AzureStorage )
    {
      throw new ArgumentException(
        Resources.AzureFileUnsupportedBackupManagementTypeException );
    }
    serviceClientProviderType = ServiceClientHelpers.
      GetServiceClientProviderType( Models.WorkloadType.AzureFiles );
  }
  ....
}

Dos más si los bloques son absolutamente idénticos, incluyendo tanto la condición como el cuerpo del bloque. Dichos errores generalmente se cometen cuando se usa el método de copiar y pegar. El problema aquí es nuevamente la criticidad del error. Si no se trata de una simple duplicación de código, puede ser la verificación necesaria ausente y el conjunto de acciones adecuado. El autor definitivamente tiene que editar el código.

V3005 La variable 'this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent' se asigna a sí misma. SetAzureVMOperatingSystemCommand.cs 298 (Computación)

public override void ExecuteCmdlet()
{
  ....
  // OS Profile
  this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent =
    this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent;
  ....
}

El valor del inmueble es autoasignable. Echa un vistazo a su declaración:

[JsonProperty(PropertyName = "provisionVMAgent")]
public bool? ProvisionVMAgent { get; set; }

La Propiedad Json la descripción indica:"Instruye al Newtonsoft.Json.JsonSerializer para que siempre serialice el miembro con el nombre especificado". Parece que todo es inocente y se ha cometido el error evidente. Uso explícito de this acceder a la propiedad también es bastante confuso. Tal vez, no se especificó por error otra variable en lugar de esta. Pero no saquemos conclusiones precipitadas. El hecho es que encontré muchas asignaciones de este tipo (una propiedad se autoasigna). Aquí hay un ejemplo de una asignación, muy similar a un error:

V3005 La variable 'this.LastHeartbeat' se asigna a sí misma. PSFabricDetails.cs 804 (Servicios de recuperación)

public ASRInMageAzureV2SpecificRPIDetails(
  InMageAzureV2ReplicationDetails details)
{
  this.LastHeartbeat = this.LastHeartbeat;  // <=
  this.RecoveryAvailabilitySetId = details.RecoveryAvailabilitySetId;
  this.AgentVersion = details.AgentVersion;
  this.DiscoveryType = details.DiscoveryType;
  ....
}

Echemos un vistazo más de cerca a la segunda asignación y las subsiguientes. En la parte derecha de la expresión, detalles tiene lugar en lugar de esto. Ahora mira la declaración de this.LastHeartbeat propiedad:

public DateTime? LastHeartbeat { get; set; }

Por último, busquemos la propiedad con el mismo nombre en InMageAzureV2ReplicationDetails clase. Tal propiedad se declara allí:

public class InMageAzureV2ReplicationDetails :
  ReplicationProviderSpecificSettings
{
  ....
  [JsonProperty(PropertyName = "lastHeartbeat")]
  public DateTime? LastHeartbeat { get; set; }
  ....  
}

Bueno, en este caso, estoy dispuesto a admitir que es un verdadero error. Pero, ¿qué haremos con las próximas advertencias? A diferencia de dos fragmentos de código anteriores, hay varias propiedades autoasignadas. Bueno, esto parece menos un error:

  • V3005 La variable 'this.ResourceGroupName' se asigna a sí misma. EliminarAzureRmExpressRouteConnectionCommand.cs 84 (CognitiveServices)
  • V3005 La variable 'this.ExpressRouteGatewayName' se asigna a sí misma. EliminarAzureRmExpressRouteConnectionCommand.cs 85 (CognitiveServices)
  • V3005 La variable 'this.Name' se asigna a sí misma. EliminarAzureRmExpressRouteConnectionCommand.cs 86 (CognitiveServices)
[Cmdlet(VerbsCommon.Remove,
  ResourceManager.Common.AzureRMConstants.AzureRMPrefix +
    "ExpressRouteConnection",
  DefaultParameterSetName =
    CortexParameterSetNames.ByExpressRouteConnectionName,
  SupportsShouldProcess = true),
  OutputType(typeof(bool))]
public class RemoveExpressRouteConnectionCommand :
  ExpressRouteConnectionBaseCmdlet
{
  [Parameter(
    Mandatory = true,
    ParameterSetName = CortexParameterSetNames.ByExpressRouteConnectionName,
    HelpMessage = "The resource group name.")]
  [ResourceGroupCompleter]
  [ValidateNotNullOrEmpty]
  public string ResourceGroupName { get; set; }
  ....
  public override void Execute()
  {
    if (....)
    {
      this.ResourceGroupName = this.ResourceGroupName;
      this.ExpressRouteGatewayName = this.ExpressRouteGatewayName;
      this.Name = this.Name;
    }
    ....
  }    
  ....
}

Ejecutar El método contiene autoasignaciones de tres propiedades seguidas. Por si acaso, cité la declaración completa de la clase RemoveExpressRouteConnectionCommand y todos sus atributos, así como el ResourceGroupName declaración de propiedad (otras dos propiedades se declaran de manera similar). Fueron estas advertencias las que me hicieron pensar en la pregunta:"¿Es un error?" Sospecho que algo de magia interna del desarrollo de PowerShell puede estar ocurriendo aquí. Espero que entre los lectores haya expertos que estén informados sobre este tema. No estoy listo para sacar ninguna conclusión en este caso.

V3006 El objeto fue creado pero no está siendo utilizado. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 259 (RecoveryServices)

private void StartRPITestFailover()
{
  ....
  if (....)
  {
    ....
  }
  else
  {
    new ArgumentException(
      Resources
        .UnsupportedDirectionForTFO); // Throw Unsupported Direction
                                      // Exception
  }
  ....
}

El tiro se omite la palabra clave. Y el comentario dice que la excepción solo tiene que ser lanzada. Encontré varios errores más similares en RecoveryServices solución:

  • V3006 El objeto se creó pero no se está utilizando. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 305 (RecoveryServices)
  • V3006 El objeto se creó pero no se está utilizando. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 278 (RecoveryServices)
  • V3006 El objeto se creó pero no se está utilizando. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 322 (RecoveryServices)
  • V3006 El objeto se creó pero no se está utilizando. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 421 (RecoveryServices)
  • V3006 El objeto se creó pero no se está utilizando. Podría faltar la palabra clave 'lanzar':lanzar nueva ArgumentException(FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 452 (RecoveryServices)

V3022 La expresión 'apiType.HasValue' siempre es falsa. ApiManagementClient.cs 1134 (ApiManagement)

private string GetApiTypeForImport(...., PsApiManagementApiType? apiType)
{
  ....
  if (apiType.HasValue)
  {
    switch(apiType.Value)
    {
      case PsApiManagementApiType.Http: return SoapApiType.SoapToRest;
      case PsApiManagementApiType.Soap: return SoapApiType.SoapPassThrough;
      default: return SoapApiType.SoapPassThrough;
    }
  }

  return apiType.HasValue ?        // <=
    apiType.Value.ToString("g") : 
    PsApiManagementApiType.Http.ToString("g");
}

La lógica del trabajo se ha roto. Si apiType contiene un valor, el control no alcanzará el return expresión al final del método (todas switch las ramas contienen return ). De lo contrario, el método siempre devolverá PsApiManagementApiType.Http.ToString("g") , mientras que apiType.Value.ToString("g") nunca se devolverá el valor.

V3022 La expresión 'automationJob !=null &&AutomationJob ==null' siempre es falsa. NodeConfigurationDeployment.cs 199 (Automatización)

public NodeConfigurationDeployment(
  ....,
  Management.Automation.Models.Job automationJob = null, 
  ....)
{
  ....
  if (automationJob != null && automationJob == null) return;
  ....
}

Código contrario a la intuición. Dos cheques que se contradicen. Probablemente la segunda comprobación de null contiene la variable incorrecta.

V3022 La expresión siempre es falsa. DataFactoryClient.Encrypt.cs 37 (Fábrica de datos)

public virtual string OnPremisesEncryptString(....)
{
  ....
  if ( linkedServiceType == LinkedServiceType.OnPremisesSqlLinkedService 
    && linkedServiceType == LinkedServiceType.OnPremisesOracleLinkedService
    && linkedServiceType == LinkedServiceType.OnPremisesFileSystemLinkedService
    && (value == null || value.Length == 0))
  {
    throw new ArgumentNullException("value");
  }
  ....
}

La verificación no tiene sentido y la excepción nunca se lanzará. La condición requiere linkedServiceType simultáneos igualdad de la variable a tres valores diferentes. Los operadores &&y || es probable que se confundan. Código fijo:

if (( linkedServiceType == LinkedServiceType.OnPremisesSqlLinkedService 
  || linkedServiceType == LinkedServiceType.OnPremisesOracleLinkedService
  || linkedServiceType == LinkedServiceType.OnPremisesFileSystemLinkedService)
  && (value == null || value.Length == 0))
....

V3022 La expresión 'Ekus ==null' siempre es falsa. PSKeyVaultCertificatePolicy.cs 129 (KeyVault)

internal CertificatePolicy ToCertificatePolicy()
{
  ....
  if (Ekus != null)
  {
    x509CertificateProperties.Ekus = Ekus == null ? 
      null : new List<string>(Ekus);
  }                
  ....
}

Comprobación redundante de los Ekus variable para null . Probablemente esté bien, pero el código no se ve bien.

V3023 Considere inspeccionar esta expresión. La expresión es excesiva o contiene un error tipográfico. PolicyRetentionObjects.cs 207 (Servicios de recuperación)

public virtual void Validate()
{
  if (RetentionTimes == null 
    || RetentionTimes.Count == 0 
    || RetentionTimes.Count != 1)
  {
    throw new ArgumentException(
      Resources.InvalidRetentionTimesInPolicyException);
  }
}

Aquí hay un control excesivo o una condición excesiva. La verificación RetentionTimes.Count ==0 no tiene sentido, ya que después de eso, la verificación RetentionTimes.Count !=1 sigue.

V3025 Formato incorrecto. Se espera un número diferente de elementos de formato al llamar a la función 'Formato'. Argumentos no utilizados:this.ResourceGroupName. NewScheduledQueryRuleCommand.cs 117 (Supervisar)

protected override void ProcessRecordInternal()
{
  ....
  if (this.ShouldProcess(this.Name,
    string.Format("Creating Log Alert Rule '{0}' in resource group {0}",
      this.Name, this.ResourceGroupName)))
  {
    ....
  }
  ....
}

Un error en la línea de formato. El especificador {0} se usa dos veces, y el Formato al método se le pasan dos argumentos. Esta es la versión correcta:

if (this.ShouldProcess(this.Name,
  string.Format("Creating Log Alert Rule '{0}' in resource group {1}",
    this.Name, this.ResourceGroupName)))
....

Otro error similar:

  • V3025 Formato incorrecto. Se espera un número diferente de elementos de formato al llamar a la función 'Formato'. Argumentos no utilizados:this.ResourceGroupName. EliminarScheduledQueryRuleCommand.cs 88 (Supervisar)

V3042 Posible excepción de referencia nula. Los '?.' y '.' los operadores se utilizan para acceder a los miembros del objeto 'imageAndOsType' VirtualMachineScaleSetStrategy.cs 81 (Compute)

internal static ResourceConfig<VirtualMachineScaleSet> 
CreateVirtualMachineScaleSetConfig(...., ImageAndOsType imageAndOsType, ....)
{
  ....
  VirtualMachineProfile = new VirtualMachineScaleSetVMProfile
  {
    OsProfile = new VirtualMachineScaleSetOSProfile
    {
        ....,
        WindowsConfiguration = 
          imageAndOsType.CreateWindowsConfiguration(),  // <=
        ....,
    },
    StorageProfile = new VirtualMachineScaleSetStorageProfile
    {
        ImageReference = imageAndOsType?.Image,  // <=
        DataDisks = DataDiskStrategy.CreateVmssDataDisks(
          imageAndOsType?.DataDiskLuns, dataDisks)  // <=
    },  
  },
  ....
}

Al crear el VirtualMachineScaleSetVMProfile objeto, el imageAndOsType se verifica la variable para null sin ningún control previo. Sin embargo, más adelante al crear VirtualMachineScaleSetStorageProfile , esta variable ya está verificada usando el operador de acceso condicional incluso dos veces. El código no parece seguro.

V3042 Posible excepción de referencia nula. Los '?.' y '.' los operadores se utilizan para acceder a los miembros del objeto 'contactos existentes' RemoveAzureKeyVaultCertificateContact.cs 123 (KeyVault)

public override void ExecuteCmdlet()
{
  ....
  List<PSKeyVaultCertificateContact> existingContacts;
  
  try
  {
    existingContacts = this.DataServiceClient.
                       GetCertificateContacts(VaultName)?.ToList();
  }
  catch (KeyVaultErrorException exception)
  {
    ....
    existingContacts = null;
  }
  
  foreach (var email in EmailAddress)
  {
    existingContacts.RemoveAll(....);  // <=
  }
  ....
}

Tanto en la ejecución normal como como resultado del manejo de una excepción, la variable contactos existentes puede obtener el null valor, después de lo cual la ejecución continuará. Más adelante en el código, esta variable se usa sin ningún motivo específico.

V3066 Posible orden incorrecto de los argumentos pasados ​​al método 'PersistSyncServerRegistration':'storageSyncServiceUid' y 'discoveryUri'. EcsManagementInteropClient.cs 364 (StorageSync)

public class EcsManagementInteropClient : IEcsManagement
{
  ....
  public int PersistSyncServerRegistration(....)
  {
    return m_managementObject.PersistSyncServerRegistration(
      serviceUri,
      subscriptionId,
      storageSyncServiceName,
      resourceGroupName,
      clusterId,
      clusterName,
      storageSyncServiceUid,  // <=
      discoveryUri,           // <=
      serviceLocation,
      resourceLocation);
  }
  ....
}

El analizador sospechó que el orden de los argumentos de PersistSyncServerRegistration se confunde el método. Declaración del método:

public interface IEcsManagement : IDisposable
{
  ....
  int PersistSyncServerRegistration(
    [In, MarshalAs(UnmanagedType.BStr)]
    string serviceUri,
    [In, MarshalAs(UnmanagedType.BStr)]
    string subscriptionId,
    [In, MarshalAs(UnmanagedType.BStr)]
    string storageSyncServiceName,
    [In, MarshalAs(UnmanagedType.BStr)]
    string resourceGroupName,
    [In, MarshalAs(UnmanagedType.BStr)]
    string clusterId,
    [In, MarshalAs(UnmanagedType.BStr)]
    string clusterName,
    [In, MarshalAs(UnmanagedType.BStr)]
    string discoveryUri,                              // <=
    [In, MarshalAs(UnmanagedType.BStr)]
    string storageSyncServiceUid,                     // <=
    [In, MarshalAs(UnmanagedType.BStr)]
    string serviceLocation,
    [In, MarshalAs(UnmanagedType.BStr)]
    string resourceLocation);
  ....
}

De hecho, algo anda mal aquí con los argumentos número siete y ocho. El autor tiene que comprobar el código.

V3077 El setter de la propiedad 'GetGuid' no utiliza su parámetro 'value'. RecoveryServicesBackupCmdletBase.cs 54 (RecoveryServices)

public abstract class RecoveryServicesBackupCmdletBase : AzureRMCmdlet
{
  ....
  static string _guid;
  
  protected static string GetGuid
  {
    get { return _guid; }
    set { _guid = Guid.NewGuid().ToString(); }
  }
  ....
}

El setter no usa el parámetro pasado. En su lugar, crea un GUID nuevo y lo asigna a _guid campo. Creo que la mayoría de los lectores estarían de acuerdo en que dicho código se ve al menos feo. Esta construcción no es muy conveniente de usar:al (re) inicializar el GetGuid propiedad, hay que asignarle un valor falso, que no es muy obvio. Pero sobre todo me divirtió la forma en que los autores usaron este patrón. Solo hay un lugar, donde GetGuid es manejado. Compruébalo:

public override void ExecuteCmdlet()
{
  ....
  var itemResponse = ServiceClientAdapter.CreateOrUpdateProtectionIntent(
    GetGuid ?? Guid.NewGuid().ToString(),
    ....);  
  ....
}

¡Brillante!

V3091 Análisis empírico. Es posible que haya un error tipográfico dentro del literal de cadena:"Id. de grupo de administración". La palabra 'Id' es sospechosa. Constants.cs 36 (Recursos)

public class HelpMessages
{
  public const string SubscriptionId = "Subscription Id of the subscription
                                        associated with the management";
  public const string GroupId = "Management Group Id";  // <=
  public const string Recurse = "Recursively list the children of the
                                 management group";
  public const string ParentId = "Parent Id of the management group";
  public const string GroupName = "Management Group Id";  // <=
  public const string DisplayName = "Display Name of the management group";
  public const string Expand = "Expand the output to list the children of the
                                management group";
  public const string Force = "Force the action and skip confirmations";
  public const string InputObject = "Input Object from the Get call";
  public const string ParentObject = "Parent Object";
}

El analizador señaló un posible error en la cadena asignada para GroupName constante. La conclusión se basa en el análisis empírico de otras asignaciones, teniendo en cuenta los nombres de las variables. Creo que en este caso el analizador tiene razón, y el valor del GroupName constante debe ser una especie de "Nombre del grupo de administración". Probablemente el error se debió a que el valor de GroupId la constante se copió, pero no se modificó.

Otro error similar:

  • V3091 Análisis empírico. Es posible que haya un error tipográfico dentro del literal de cadena. La palabra 'Nombre' es sospechosa. ParamHelpMsgs.cs 153 (Servicios de recuperación)

V3093 El '|' operador evalúa ambos operandos. Tal vez un cortocircuito '||' En su lugar, se debe utilizar el operador. PSKeyVaultCertificatePolicy.cs 114 (KeyVault)

internal CertificatePolicy ToCertificatePolicy()
{
  ....
  if (!string.IsNullOrWhiteSpace(SubjectName) ||
    DnsNames != null ||
    Ekus != null ||
    KeyUsage != null |        // <=
    ValidityInMonths.HasValue)
  {
    ....
  }
  ....
}

En este fragmento puede ocurrir un error y en el if bloque entre dos últimas condiciones el || podría haberse utilizado el operador. Pero como suele suceder, solo el desarrollador puede dar la respuesta correcta.

V3095 El objeto 'certificado' se usó antes de que se verificara contra nulo. Verifique las líneas:41, 43. CertificateInfo.cs 41 (Automatización)

public CertificateInfo(
  ...., 
  Azure.Management.Automation.Models.Certificate certificate)
{
  ....
  this.Name = certificate.Name;
  
  if (certificate == null) return;
  ....
}

Clásico. Primero se usa el objeto y solo después de eso se verifica la referencia para null . Nos encontramos con este tipo de errores muy a menudo. Consideremos otro error similar.

V3095 El objeto 'clusterCred' se usó antes de que se verificara contra nulo. Verifique las líneas:115, 118. InvokeHiveCommand.cs 115 (HDInsight)

public override void ExecuteCmdlet()
{
  ....
  _credential = new BasicAuthenticationCloudCredentials
  {
    Username = clusterCred.UserName,
    Password = clusterCred.Password.ConvertToString()
  };
  
  if (clusterConnection == null || clusterCred == null)
  ....
}

Aquí hay un par de errores similares:

  • V3095 El objeto '_profile' se usó antes de que se verificara contra nulo. Verifique las líneas:47, 49. RMProfileClient.cs 47 (Cuentas)
  • V3095 El objeto 'this.LoadBalancer.BackendAddressPools' se usó antes de que se verificara contra nulo. Verifique las líneas:56, 63. AddAzureRmLoadBalancerBackendAddressPoolConfigCommand.cs 56 (CognitiveServices)
  • En términos generales, vi muchos errores V3095 en el código de Azure PowerShell. Pero todos ellos son bastante similares, por lo que no me detendré en este tema.

V3125 El objeto 'startTime' se usó después de que se verificó contra nulo. Verifique las líneas:1752, 1738. AutomationPSClientDSC.cs 1752 (Automatización)

private string GetNodeReportListFilterString(
  ....,
  DateTimeOffset? startTime,
  ....,
  DateTimeOffset? lastModifiedTime)
{
  ....
  if (startTime.HasValue)
  {
    odataFilter.Add("properties/startTime ge " +
      this.FormatDateTime(startTime.Value));      // <=
  }
  ....
  if (lastModifiedTime.HasValue)
  {
    odataFilter.Add("properties/lastModifiedTime ge " +
      this.FormatDateTime(startTime.Value));      // <=
  }
  ....
}

También es un tipo de error bastante extendido. La hora de inicio la variable se comprueba en busca de una presencia de valor cuando se utiliza por primera vez. Pero no se hace en el uso posterior. Bueno, la situación puede ser aún peor. Mira el segundo si bloquear. Creo que el startTime variable no debe estar aquí en absoluto. En primer lugar, no se verifica la presencia de un valor antes de su uso. En segundo lugar, la cadena formada para pasar a Add método también confirma mi sugerencia. Otra variable (lastModifiedTime ) se menciona en la primera parte de esta cadena.

V3125 El objeto 'firstPage' se usó después de que se verificó contra nulo. Verifique las líneas:113, 108. IntegrationAccountAgreementOperations.cs 113 (LogicApp)

public IList<IntegrationAccountAgreement> 
ListIntegrationAccountAgreements(....)
{
  var compositeList = new List<IntegrationAccountAgreement>();
  var firstPage = this.LogicManagementClient.
                  IntegrationAccountAgreements.List(....);

  if (firstPage != null)
  {
    compositeList.AddRange(firstPage);
  }

  if (!string.IsNullOrEmpty(firstPage.NextPageLink))  // <=
  {
    ....
  }
  ....
}

Otro error evidente. La primera página la variable se usa de manera insegura a pesar del hecho de que anteriormente en el código esta variable ya se usó y se verificó preliminarmente para null .

Encontré incluso más advertencias V3125 en el código de Azure PowerShell que las V3095 descritas anteriormente. Todos ellos son también del mismo tipo. Creo que dos de los que hemos considerado son suficientes.

V3137 La variable 'apiVersionSetId' se asigna pero no se usa al final de la función. GetAzureApiManagementApiVersionSet.cs 69 (ApiManagement)

public String ApiVersionSetId { get; set; }
....
public override void ExecuteApiManagementCmdlet()
{
  ....
  string apiVersionSetId;

  if (ParameterSetName.Equals(ContextParameterSet))
  {
    ....
    apiVersionSetId = ApiVersionSetId;
  }
  else
  {
    apiVersionSetId = ....;
  }

  if (string.IsNullOrEmpty(ApiVersionSetId))  // <=
  {
    WriteObject(....);
  }
  else
  {
    WriteObject(Client.GetApiVersionSet(...., ApiVersionSetId))  // <=
  }
}

El analizador informa que apiVersionSetId La variable local se inicializó, pero no se usó de ninguna manera. A menudo, este patrón indica un error. Creo que en este caso lo más probable es que se trate de un error, especialmente teniendo en cuenta el hecho de que el nombre del apiVersionSetId variable local y el nombre del ApiVersionSetId propiedad difieren sólo por el caso de la primera letra. Echa un vistazo al código. Después de inicializar apiVersionSetId (de una forma u otra), solo el ApiVersionSetId La propiedad se usa más en el código. Parece extremadamente sospechoso.

V3137 La variable 'cacheId' se asigna pero no se usa al final de la función. EliminarAzureApiManagementCache.cs 94 (ApiManagement)

public String CacheId { get; set; }
....
public override void ExecuteApiManagementCmdlet()
{
  ....
  string cacheId;

  if (....)
  {
    ....
    cacheId = InputObject.CacheId;
  }
  else if (....)
  {
    ....
    cacheId = cache.CacheId;
  }
  else
  {
    ....
    cacheId = CacheId;
  }
  var actionDescription = string.Format(...., CacheId);   // <=
  var actionWarning = string.Format(...., CacheId);       // <=
  ....
  Client.CacheRemove(resourceGroupName, serviceName, CacheId);  // <=
  ....  
}

Este es el caso que es casi el mismo que el descrito anteriormente. El cachéId la variable local no se usa después de la inicialización de ninguna manera. En cambio, otra propiedad con un nombre muy similar CacheId se usa No estoy seguro, puede ser que sea solo un patrón de programación de los desarrolladores de Azure PowerShell. De todos modos, parece un error.

V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. NewAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)

[Parameter(Mandatory = false, 
  HelpMessage = "The integration account partner type.",
  ValueFromPipelineByPropertyName = false)]
[ValidateSet("B2B", IgnoreCase = false)]
[ValidateNotNullOrEmpty]
public string PartnerType
{
  get { return this.partnerType; }
  set { value = this.partnerType; }  // <=
}

El tipo de socio campo se declara de la siguiente manera:

/// <summary>
/// Default partner type.
/// </summary>
private string partnerType = "B2B";

A pesar del nombre de la solución (LogicApp) donde se detectó un error, no le encuentro lógica. Modificando valor en el colocador no es raro, pero en este caso se trata de una pérdida del valor original. se ve raro En el código la propiedad se lee una sola vez. Tal vez, debamos volver a pedir el consejo de los expertos. Tal vez simplemente no lo entiendo. El punto es que encontré varios patrones iguales:

  • V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. NuevoAzureIntegrationAccountSchemaCommand.cs 79 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. NuevoAzureIntegrationAccountSchemaCommand.cs 87 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. ActualizarAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. ActualizarAzureIntegrationAccountSchemaCommand.cs 80 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un definidor de propiedades y no se usa después de eso. ActualizarAzureIntegrationAccountSchemaCommand.cs 88 (LogicApp)

Conclusión

Todos estos son errores interesantes que se encontraron en el código de Azure PowerShell. Los entusiastas y aquellos que estén interesados ​​pueden revisar los errores ellos mismos en este (o cualquier otro) proyecto. Probablemente podría perderme algo fuera de lo común. Para hacer la revisión, solo necesita descargar e instalar PVS-Studio.

Gracias por leer hasta el final. Y, por supuesto, ¡código sin errores para todos!