Azure PowerShell: "principalmente inofensivo"

Quadro 6

Olá pessoal. Hoje temos outro projeto da Microsoft em teste. Pelo título do artigo, você pode supor que desta vez os desenvolvedores não puderam nos agradar com um grande número de erros. Esperamos que os autores do projeto não sejam ofendidos pelo nome. Afinal, um pequeno número de erros é excelente, não é? No entanto, algo interessante foi encontrado no código do Azure PowerShell. Sugerimos que você se familiarize com os recursos deste projeto e observe os erros encontrados no analisador PV # C PVS-Studio.

Sobre o projeto


O Azure PowerShell é um cmdlet que permite gerenciar recursos do Azure diretamente da linha de comando do PowerShell . O principal objetivo deste conjunto é simplificar o aprendizado e o início rápido do desenvolvimento do Azure. O Azure PowerShell fornece aos administradores e desenvolvedores recursos avançados para criar, implantar e gerenciar aplicativos do Microsoft Azure.

O Azure PowerShell é desenvolvido no ambiente .NET Standard e é suportado pelo PowerShell 5.1 para Windows e PowerShell 6.xe superior para todas as plataformas. O código-fonte do Azure PowerShell está disponível no GitHub.

Recentemente, os projetos da Microsoft costumam me chamar a atenção. Na minha opinião, a qualidade desses projetos geralmente é a melhor. Embora, é claro, não sem exceções, como foi o artigo " WinForms: erros, Holmes ". No entanto, desta vez tudo está normal. O projeto é grande: 6845 arquivos. Código-fonte .cs contêm cerca de 700 mil linhas, exceto vazias (testes, bem como avisos do terceiro nível de confiabilidade, não levei em consideração). Havia muito poucos erros para esse volume de código: não mais que cem. Existem muitas situações do mesmo tipo, portanto, para o artigo, selecionei os aspectos mais interessantes. Os erros, como normalmente faço, são classificados pelos números de diagnóstico do PVS-Studio.

Também deparei com fragmentos de código que parecem erros, mas não consegui concluir inequivocamente a existência de um problema real, pois não estou familiarizado o suficiente com os recursos de desenvolvimento do PowerShell. Espero que entre os leitores haja especialistas neste assunto e que me ajude. Sobre isso abaixo.

Antes do início da "análise", observo a peculiaridade do projeto em termos de sua estrutura. O código-fonte do Azure PowerShell consiste em mais de setenta soluções do Visual Studio. Algumas soluções incluem projetos de outras soluções. Tal estrutura atrasou um pouco a análise (não muito). O restante da verificação não causou dificuldades. Por conveniência, na linha da mensagem de erro (entre colchetes), indicarei o nome da solução na qual esse erro foi detectado.

Resultados da validação


V3001 Existem sub-expressões idênticas 'strTimespan.Contains ("M")' à esquerda e à direita da '||' 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")) .... } 

Um exemplo de erro bastante óbvio, que somente o desenvolvedor pode corrigir. Nesse caso, é completamente incompreensível se estamos lidando com a duplicação de código que não afeta nada ou, em vez de "M", algo mais deve aparecer em uma das duas últimas verificações.

V3001 Existem subexpressões idênticas 'this.AggregationType! = Null' à esquerda e à direita do operador '&&'. GetAzureRmMetricCommand.cs 156 (Monitor)

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

Provavelmente não há erro aqui. Este é um exemplo de código redundante. Às vezes, esse código pode indicar um nível insuficiente de conhecimento do desenvolvedor. O fato é que as verificações this.AggregationType! = Null e this.AggregationType.HasValue são idênticas. Basta usar um deles (qualquer um). Pessoalmente, prefiro a opção HasValue :

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

V3003 O uso do padrão 'if (A) {...} else if (A) {...}' foi detectado. Há uma probabilidade de presença de erro lógico. Verifique as linhas: 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 ); } .... } 

Os outros dois blocos se são absolutamente idênticos, incluindo a condição e o corpo do bloco. Esses erros geralmente são cometidos ao usar o método copiar e colar no trabalho. A questão novamente é a criticidade desse erro. Se essa não é uma simples duplicação de código, podemos falar sobre a falta da verificação necessária e o conjunto de ações correspondente. O autor precisa editar o código.

V3005 A variável 'this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent' é atribuída a si mesma. SetAzureVMOperatingSystemCommand.cs 298 (Compute)

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

O valor da propriedade é atribuído a si próprio. Dê uma olhada no anúncio dele:

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

A descrição JsonProperty diz "Instrui o Newtonsoft.Json.JsonSerializer para sempre serializar o membro com o nome especificado". Parece que tudo é simples e há um erro óbvio. Também é confuso o uso explícito disso para acessar uma propriedade. Talvez, por engano, alguma outra variável não tenha sido especificada em vez disso . Mas, por enquanto, não tiraremos conclusões. O fato é que, no código do Azure PowerShell, conheci muitas dessas atribuições (a propriedade é atribuída a si mesma). Aqui está outro exemplo de uma atribuição que é muito semelhante a um erro:

V3005 A variável 'this.LastHeartbeat' é atribuída a si mesma. PSFabricDetails.cs 804 (RecoveryServices)

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

Preste atenção na segunda e nas seguintes tarefas. No lado direito da expressão, não é isso que aparece, mas detalhes . Dê uma olhada na declaração desta propriedade.LastHeartbeat:

 public DateTime? LastHeartbeat { get; set; } 

Por fim, procure uma propriedade com o mesmo nome na classe InMageAzureV2ReplicationDetails . Essa propriedade é declarada lá:

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

Bem, neste caso, estou pronto para admitir que esse gatilho é um erro real. Mas o que fazer com os seguintes gatilhos? Neles, diferentemente dos dois fragmentos de código anteriores, há uma atribuição múltipla de propriedades para si mesmos. Isso já é menos como um erro:

  • V3005 A variável 'this.ResourceGroupName' é atribuída a si mesma. RemoveAzureRmExpressRouteConnectionCommand.cs 84 (CognitiveServices)
  • V3005 A variável 'this.ExpressRouteGatewayName' é atribuída a si mesma. RemoveAzureRmExpressRouteConnectionCommand.cs 85 (CognitiveServices)
  • V3005 A variável 'this.Name' é atribuída a si mesma. RemoveAzureRmExpressRouteConnectionCommand.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; } .... } .... } 

O método Execute contém três atribuições consecutivas de propriedades para elas mesmas. Por precaução, dei uma declaração completa da classe RemoveExpressRouteConnectionCommand com todos os seus atributos, bem como uma declaração da propriedade ResourceGroupName (as outras duas propriedades são declaradas da mesma maneira). Foi tão desencadeador que me fez pensar sobre a pergunta: "Isso é um erro?" Suspeito que algum tipo de mágica interna do desenvolvimento do PowerShell possa acontecer aqui. Espero que entre os leitores haja especialistas com conhecimento sobre esse assunto. Não estou pronto para concluir neste caso.

V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 259 (RecoveryServices)

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

Ignorou a palavra-chave throw . Além disso, o comentário diz que a exceção deve ser lançada. Na solução RecoveryServices , encontrei vários erros semelhantes:

  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 305 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 278 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 322 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 421 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 452 (RecoveryServices)

A expressão V3022 'apiType.HasValue' é sempre 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"); } 

A lógica do trabalho é violada. Se apiType contiver um valor, o controle não alcançará a expressão de retorno no final do método (todas as ramificações do comutador contêm retorno ). Caso contrário, o método sempre retornará PsApiManagementApiType.Http.ToString ("g") e apiType.Value.ToString ("g") , respectivamente, nunca serão retornados.

A expressão V3022 'automationJob! = Null && automationJob == null' é sempre falsa. NodeConfigurationDeployment.cs 199 (automação)

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

O código paradoxal. Dois cheques que se contradizem. Provavelmente, a segunda verificação de igualdade nula contém a variável errada.

A expressão V3022 é sempre falsa. DataFactoryClient.Encrypt.cs 37 (DataFactory)

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

A verificação é inútil e uma exceção nunca será lançada. A condição requer a igualdade simultânea da variável linkedServiceType para três valores diferentes. Os operadores && e || provavelmente estão confusos. Código corrigido:

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

A expressão V3022 'Ekus == null' é sempre falsa. PSKeyVaultCertificatePolicy.cs 129 (KeyVault)

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

Verificação supérflua da variável Ekus para igualdade nula . Provavelmente nada de errado, mas o código parece feio.

V3023 Considere inspecionar esta expressão. A expressão é excessiva ou contém uma impressão incorreta. PolicyRetentionObjects.cs 207 (RecoveryServices)

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

Verificação excessiva ou condição incorreta. Verificar RetentionTimes.Count == 0 não faz sentido, porque depois disso, verifique RetentionTimes.Count! = 1 .

V3025 Formato incorreto. É esperado um número diferente de itens de formato ao chamar a função 'Format'. Argumentos não utilizados: this.ResourceGroupName. NewScheduledQueryRuleCommand.cs 117 (Monitor)

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

Erro na cadeia de formatação. O qualificador {0} é usado duas vezes, com dois argumentos passados ​​para o método Format . A opção correta:

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

Outro erro semelhante:

  • V3025 Formato incorreto. É esperado um número diferente de itens de formato ao chamar a função 'Format'. Argumentos não utilizados: this.ResourceGroupName. RemoveScheduledQueryRuleCommand.cs 88 (Monitor)

V3042 Possível NullReferenceException. O '?' e '.' operadores são usados ​​para acessar membros do 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) // <= }, }, .... } 

Ao criar um objeto VirtualMachineScaleSetVMProfile , a variável imageAndOsType é usada sem verificar se é nulo . No entanto, ao criar o VirtualMachineScaleSetStorageProfile , essa variável já é verificada usando o operador de acesso condicional e duas vezes. O código parece inseguro.

V3042 Possível NullReferenceException. O '?' e '.' operadores são usados ​​para acessar membros do objeto 'existenteContacts' 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(....); // <= } .... } 

Como no caso da execução normal, e como resultado do tratamento da exceção, a variável existenteContacts pode ficar nula , após o que o trabalho continuará. Além disso, no código, essa variável é usada sem qualquer verificação.

V3066 Possível ordem incorreta de argumentos passada para o método 'PersistSyncServerRegistration': 'storageSyncServiceUid' e '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); } .... } 

O analisador suspeitou que a ordem de transmissão de argumentos para o método PersistSyncServerRegistration estivesse confusa . Declaração do 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 fato, algo está misturado aqui com os argumentos número sete e oito. A verificação do código pelo autor é necessária.

V3077 O configurador da propriedade 'GetGuid' não utiliza seu 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(); } } .... } 

O setter não usa o parâmetro transmitido. Em vez disso, eles obtêm um novo GUID e o atribuem ao campo _guid . Eu acho que a maioria dos leitores concorda que esse código parece pelo menos feio. Usar essa construção não é muito conveniente: para (re) inicializar a propriedade GetGuid , você deve atribuir algum valor falso a ela, o que é completamente óbvio. Mas, acima de tudo, fiquei divertido no momento em que os próprios autores usam essa construção. Há apenas um lugar no código em que o GetGuid funciona . Dê uma olhada:

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

Ótimo!

V3091 Análise empírica. É possível que um erro de digitação esteja presente dentro da string literal: "ID do grupo de gerenciamento". A palavra 'Id' é suspeita. 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"; } 

O analisador indicou um possível erro na sequência atribuída para a constante GroupName . A conclusão é baseada em uma análise empírica de outras atribuições, levando em consideração nomes de variáveis. Penso que, neste caso, o analisador está correto e o valor da constante GroupName deve estar no formato "Nome do grupo de gerenciamento". Provavelmente o erro foi causado pelo fato de o valor da constante GroupId ter sido copiado , mas eles esqueceram de alterá-lo.

Outro erro semelhante:

  • V3091 Análise empírica. É possível que um erro de digitação esteja presente dentro da string literal. A palavra 'Nome' é suspeita. ParamHelpMsgs.cs 153 (RecoveryServices)

V3093 O '|' O operador avalia os dois operandos. Talvez um curto-circuito '||' operador deve ser usado. PSKeyVaultCertificatePolicy.cs 114 (KeyVault)

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

Há suspeita de que tenha sido cometido um erro, e o operador || também deve ser usado entre as últimas condições no bloco if . A resposta exata, como muitas vezes acontece, só pode ser dada pelo desenvolvedor.

V3095 O objeto 'certificado' foi usado antes de ser verificado em relação a nulo. Verifique as linhas: 41, 43. CertificateInfo.cs 41 (Automação)

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

Clássico O objeto é usado primeiro e somente então o link é verificado como nulo . Encontramos esses erros com muita frequência . Considere outro erro semelhante.

V3095 O objeto 'clusterCred' foi usado antes de ser verificado em relação a nulo. Verifique as linhas: 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) .... } 

E mais alguns erros semelhantes:

  • V3095 O objeto '_profile' foi usado antes de ser verificado contra nulo. Verifique as linhas: 47, 49. RMProfileClient.cs 47 (Contas)
  • V3095 O objeto 'this.LoadBalancer.BackendAddressPools' foi usado antes de ser verificado como nulo. Verifique as linhas: 56, 63. AddAzureRmLoadBalancerBackendAddressPoolConfigCommand.cs 56 (CognitiveServices)
  • Em geral, no código do Azure PowerShell, encontrei muito mais erros do V3095 . Mas todos são bem parecidos, então não vou me debruçar sobre isso com mais detalhes.

V3125 O objeto 'startTime' foi usado após a verificação contra nulo. Verifique as linhas: 1752, 1738. AutomationPSClientDSC.cs 1752 (Automation)

 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)); // <= } .... } 

Também é um tipo de erro bastante comum. A variável startTime foi verificada quanto ao valor no primeiro uso. A próxima vez que você usar isso, não. Mas poderia ser ainda pior. Dê uma olhada no segundo bloco se . Eu acho que não deve haver uma variável startTime . Isso é indicado pela ausência de sua verificação quanto à presença de um valor antes do uso e pela sequência formada para passar ao método Add . A primeira parte desta linha menciona outra variável ( lastModifiedTime ).

V3125 O objeto 'firstPage' foi usado depois de verificado contra nulo. Verifique as linhas: 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)) // <= { .... } .... } 

Outro erro óbvio. A variável firstPage é usada de maneira insegura, embora anteriormente no código haja um uso dessa variável com uma verificação preliminar de null .

A V3125 no código do Azure PowerShell foi ainda mais bem - sucedida que a V3095 descrita anteriormente. Todos eles também são do mesmo tipo. Acho que podemos nos limitar aos dois que examinamos.

V3137 A variável 'apiVersionSetId' é atribuída, mas não é usada no final da função. 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)) // <= } } 

O analisador relata que a variável local apiVersionSetId foi inicializada, mas não foi usada. Frequentemente, esse padrão indica um erro. Penso que, neste caso, a probabilidade de erro é alta, dado que o nome da variável local apiVersionSetId e o nome da propriedade ApiVersionSetId diferem apenas no caso da primeira letra. Dê uma olhada no código. Após inicializar apiVersionSetId (de uma maneira ou de outra), somente a propriedade ApiVersionSetId é usada no código. Muito, muito suspeito.

V3137 A variável 'cacheId' é atribuída, mas não é usada no final da função. RemoveAzureApiManagementCache.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); // <= .... } 

A situação, quase exatamente repetindo o anteriormente considerado. A variável local cacheId não é usada após a inicialização. Em vez disso, eles usam uma propriedade com um nome muito semelhante, CacheId . Não sei, talvez esse seja um padrão entre os desenvolvedores do Azure PowerShell. Mas parece um erro.

V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. 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; } // <= } 

O campo partnerType é declarado assim:

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

Ao contrário do nome da solução na qual esse erro foi detectado (LogicApp), não vejo a lógica aqui. Usar valor em um setter para gravação não é uma ocorrência tão rara, mas, neste caso, estamos falando em perder o valor original. Parece estranho. O código tem um único acesso de leitura a essa propriedade. Talvez você deva novamente procurar aconselhamento especializado. Talvez eu não entenda alguma coisa. O fato é que conheci mais alguns dos mesmos padrões:

  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. NewAzureIntegrationAccountSchemaCommand.cs 79 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. NewAzureIntegrationAccountSchemaCommand.cs 87 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountSchemaCommand.cs 80 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountSchemaCommand.cs 88 (LogicApp)

Conclusão


Esses são todos os bugs interessantes encontrados no código do Azure PowerShell.Entusiastas e apenas interessados, sugiro realizar um estudo independente de erros no código deste (ou de qualquer outro) projeto. Eu provavelmente poderia ter perdido outra coisa interessante. Para fazer isso, basta baixar e instalar o PVS-Studio .

Obrigado pela leitura. Um código sem código!



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Khrenov. Azure PowerShell: Principalmente inofensivo .

Source: https://habr.com/ru/post/pt470730/


All Articles