أزور باور شيل: "معظمها غير ضارة"

صورة 6

مرحبا بالجميع. اليوم لدينا مشروع Microsoft آخر في الاختبار. حسب عنوان المقال ، يمكنك تخمين أنه في هذه المرة لم يستطع المطورون إرضاءنا بعدد كبير من الأخطاء. نأمل ألا يسيء مؤلفو المشروع بالاسم. بعد كل شيء ، عدد قليل من الأخطاء ممتاز ، أليس كذلك؟ ومع ذلك ، تم العثور على شيء مثير للاهتمام في رمز Azure PowerShell. نقترح أن تتعرف على ميزات هذا المشروع وأن تنظر إلى الأخطاء التي تم العثور عليها باستخدام PVS-Studio PVS-analys.

عن المشروع


Azure PowerShell عبارة عن أمر cmdlet يتيح لك إدارة موارد Azure مباشرةً من سطر أوامر PowerShell . الغرض الرئيسي من هذه المجموعة هو تبسيط التعلم والبدء السريع في تطوير Azure. يوفر Azure PowerShell للمسؤولين والمطورين ميزات قوية لإنشاء تطبيقات Microsoft Azure ونشرها وإدارتها.

تم تطوير Azure PowerShell في بيئة .NET Standard ويدعمه PowerShell 5.1 لنظام التشغيل Windows و PowerShell 6.x والإصدارات الأحدث لجميع الأنظمة الأساسية. شفرة المصدر أزور PowerShell متاحة على جيثب.

في الآونة الأخيرة ، غالبًا ما تلفت انتباهي مشاريع Microsoft. في رأيي ، جودة هذه المشاريع عادة ما تكون في أفضل حالاتها. على الرغم من ، بالطبع ، ليس من دون استثناءات ، كما كان المقال " WinForms: أخطاء ، هولمز ". ومع ذلك ، هذه المرة كل شيء طبيعي. المشروع كبير: يحتوي 6845 ملف .cs شفرة المصدر على حوالي 700 ألف سطر ، باستثناء فارغة (اختبارات ، وكذلك تحذيرات من المستوى الثالث من الموثوقية ، وأنا لم تأخذ في الاعتبار). كان هناك عدد قليل جدا من الأخطاء لمثل هذا الكود من التعليمات البرمجية: ليس أكثر من مائة. هناك الكثير من المواقف من نفس النوع ، لذلك بالنسبة للمقال ، اخترت الإيجابيات الأكثر إثارة للاهتمام. يتم فرز الأخطاء ، كما أفعل عادةً ، بواسطة أرقام تشخيص PVS-Studio.

لقد صادفت أيضًا أجزاء من التعليمات البرمجية تبدو وكأنها أخطاء ، لكنني لم أتمكن من التوصل إلى نتيجة لا لبس فيها حول وجود مشكلة حقيقية ، حيث أنني لست على دراية كافية بميزات تصميم PowerShell. آمل أن يكون من بين القراء خبراء في هذا الأمر وسيساعدني. عن ذلك أدناه.

قبل بدء "استخلاص المعلومات" ، لاحظت ميزة التصميم من حيث هيكلها. يتكون مصدر شفرة Azure PowerShell من أكثر من سبعين حل Visual Studio. بعض الحلول تشمل مشاريع من حلول أخرى. تباطأ مثل هذا الهيكل التحليل قليلا (ليس كثيرا). ما تبقى من التحقق لا يسبب صعوبات. للراحة ، في سطر رسالة الخطأ (بين قوسين) ، سأشير إلى اسم الحل الذي تم اكتشاف هذا الخطأ فيه.

نتائج التحقق من الصحة


V3001 هناك تعابير فرعية مماثلة 'strTimespan.Contains ("M")' إلى اليسار وإلى يمين '||' المشغل. 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")) .... } 

مثال على خطأ واضح إلى حد ما ، والذي يستطيع المطور إصلاحه فقط. في هذه الحالة ، يكون من غير المفهوم تمامًا ما إذا كنا نتعامل مع ازدواجية التعليمات البرمجية التي لا تؤثر على أي شيء ، أو بدلاً من "M" يجب أن يظهر شيء آخر في أحد آخر تدقيقين.

V3001 هناك تعبيرات فرعية متطابقة 'this.AggregationType! = Null' إلى اليسار وإلى يمين المشغل '&&'. GetAzureRmMetricCommand.cs 156 (شاشة)

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

ربما لا يوجد خطأ هنا. هذا مثال على الكود الزائد. في بعض الأحيان قد يشير هذا الرمز إلى مستوى غير كافٍ من معرفة المطورين. الحقيقة هي أن this.AggregationType! = Null و this.AggregationType.HasValue يتحققان متطابقان. يكفي استخدام واحد منهم (أي). أنا شخصياً أفضل خيار HasValue :

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

V3003 استخدام النموذج "if (A) {...} آخر إذا تم اكتشاف (A) {...}". هناك احتمال لوجود خطأ منطقي. خطوط التحقق: 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 ); } .... } 

الاثنان الآخران إذا كانت الكتل متطابقة تمامًا ، بما في ذلك حالة وجسم الكتلة. عادة ما تحدث مثل هذه الأخطاء عند استخدام طريقة نسخ اللصق في العمل. السؤال مرة أخرى هو مدى خطورة هذا الخطأ. إذا لم يكن هذا تكرارًا بسيطًا للرمز ، فيمكننا الحديث عن عدم وجود التحقق اللازم ومجموعة الإجراءات المناسبة. يحتاج المؤلف إلى تعديل الكود.

V3005 يتم تعيين المتغير 'this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent' لنفسه. SetAzureVMOperatingSystemCommand.cs 298 (حساب)

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

يتم تعيين قيمة الخاصية لنفسها. ألقِ نظرة على إعلانه:

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

يقول وصف JsonProperty "يوجه برنامج Newtonsoft.Json.JsonSerializer لإجراء تسلسل دائم للعضو بالاسم المحدد". يبدو أن كل شيء بسيط وهناك خطأ واضح. مربكا أيضا هو الاستخدام الصريح لهذا للوصول إلى الممتلكات. ربما ، عن طريق الخطأ ، لم يتم تحديد بعض المتغيرات الأخرى بدلاً من ذلك . لكن في الوقت الحالي ، لن نستخلص النتائج. الحقيقة هي أنه في رمز Azure PowerShell قابلت الكثير من مثل هذه المهام (تم تخصيص الخاصية لنفسها). فيما يلي مثال آخر لمهمة تشبه إلى حد بعيد خطأ:

V3005 يتم تعيين متغير "this.LastHeartbeat" لنفسه. PSFabricDetails.cs 804 (خدمات الاسترداد)

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

إيلاء الاهتمام للواجبات الثانية واللاحقة. على الجانب الأيمن من التعبير ، لا يظهر هذا على الإطلاق ، بل التفاصيل . ألقِ نظرة على الإعلان الخاص بهذه الخاصية .LastHeartbeat :

 public DateTime? LastHeartbeat { get; set; } 

أخيرًا ، ابحث عن خاصية بنفس الاسم في فئة InMageAzureV2ReplicationDetails . تم إعلان هذه الخاصية هناك:

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

حسنًا ، في هذه الحالة ، أنا مستعد للاعتراف بأن هذا التنبيه هو خطأ حقيقي. ولكن ماذا تفعل مع المشغلات التالية؟ فيها ، على عكس شريحتين سابقتين ، هناك تخصيص متعدد للخصائص لأنفسهم. هذا بالفعل يشبه الخطأ:

  • V3005 يتم تعيين المتغير 'this.ResourceGroupName' لنفسه. RemoveAzureRmExpressRouteConnectionCommand.cs 84 (الخدمات المعرفية)
  • V3005 يتم تعيين متغير "this.ExpressRouteGatewayName" لنفسه. RemoveAzureRmExpressRouteConnectionCommand.cs 85 (الخدمات المعرفية)
  • V3005 يتم تعيين متغير "this.Name" لنفسه. RemoveAzureRmExpressRouteConnectionCommand.cs 86 (الخدمات المعرفية)

 [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; } .... } .... } 

يحتوي الأسلوب Execute على ثلاثة تعيينات متتالية من الخصائص إلى أنفسهم. فقط في هذه الحالة ، أعطيت إعلانًا كاملاً للفئة RemoveExpressRouteConnectionCommand بكل سماته ، بالإضافة إلى إعلان خاص بالخاصية ResourceGroupName (تم إعلان الخواص المميزة الأخرى بالطريقة نفسها). لقد كان هذا الأمر الذي جعلني أفكر في السؤال: "هل هذا خطأ؟" أظن أن هناك نوعًا من السحر الداخلي لتطوير PowerShell يمكن أن يحدث هنا. آمل أن يكون من بين القراء خبراء لديهم دراية بهذا الأمر. لست مستعدًا لتقديم أي استنتاج في هذه الحالة.

V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 259 (خدمات الاسترداد)

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

تخطي الكلمة رمي . علاوة على ذلك ، يقول التعليق أنه يجب طرح الاستثناء فقط. في حل RecoveryServices ، قابلت العديد من الأخطاء المشابهة الأخرى:

  • V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 305 (خدمات الاسترداد)
  • V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 278 (خدمات الاسترداد)
  • V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 322 (خدمات الاسترداد)
  • V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 421 (خدمات الاسترداد)
  • V3006 تم تكوين العنصر ولكن لا يتم استخدامه. الكلمة المفتاحية "رمي" قد تكون مفقودة: رمي ArgumentException (FOO) جديد. UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 452 (خدمات الاسترداد)

تعبير V3022 "apiType.HasValue" غير صحيح دائمًا. 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"); } 

يتم انتهاك منطق العمل. إذا احتوت apiType على قيمة ، فلن يصل عنصر التحكم إلى تعبير الإرجاع في نهاية الطريقة (تحتوي جميع فروع التبديل على إرجاع ). وإلا ، فستُرجع الطريقة دائمًا PsApiManagementApiType.Http.ToString ("g") و apiType.Value.ToString ("g") ، على التوالي ، لن يتم إرجاعها أبدًا.

V3022 Expression 'automationJob! = Null && automationJob == null' غير صحيح دائمًا. NodeConfigurationDeployment.cs 199 (التنفيذ التلقائي)

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

الكود المتناقض. اثنين من الشيكات التي تتناقض مع بعضها البعض. ربما تحتوي عملية التحقق من المساواة الخالية الثانية على متغير خاطئ.

تعبير V3022 غير صحيح دائمًا. 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"); } .... } 

التحقق لا معنى له ، ولن يتم طرح استثناء. الشرط يتطلب المساواة المتزامنة للمتغير linkedServiceType إلى ثلاث قيم مختلفة. ربما يتم الخلط بين مشغلي && و || كود مصحح:

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

تعبير V3022 'Ekus == null' غير صحيح دائمًا. PSKeyVaultCertificatePolicy.cs 129 (KeyVault)

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

تحقق زائدة من متغير Ekus للمساواة فارغة . ربما لا بأس ، لكن الرمز يبدو قبيحًا.

V3023 النظر في فحص هذا التعبير. التعبير مفرط أو يحتوي على خطأ مطبعي. PolicyRetentionObjects.cs 207 (خدمات الاسترداد)

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

الاختيار المفرط ، أو الشرط الخاطئ. التحقق من RetentionTimes.Count == 0 لا معنى له ، لأنه بعد ذلك تحقق RetentionTimes.Count! = 1 .

V3025 تنسيق غير صحيح. من المتوقع وجود عدد مختلف من عناصر التنسيق أثناء استدعاء وظيفة "التنسيق". الوسيطات غير المستخدمة: this.ResourceGroupName. NewScheduledQueryRuleCommand.cs 117 (شاشة)

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

خطأ في سلسلة التنسيق. يتم استخدام {0} التصفيات مرتين ، مع تمرير وسيطين إلى طريقة التنسيق . الخيار الصحيح:

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

خطأ مشابه آخر:

  • V3025 تنسيق غير صحيح. من المتوقع وجود عدد مختلف من عناصر التنسيق أثناء استدعاء وظيفة "التنسيق". الوسيطات غير المستخدمة: this.ResourceGroupName. RemoveScheduledQueryRuleCommand.cs 88 (الشاشة)

V3042 ممكن NullReferenceException. "؟" و "." تستخدم عوامل التشغيل للوصول إلى أعضاء كائن "imageAndOsType" VirtualMachineScaleSetStrategy.cs 81 (حساب)

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

عند إنشاء كائن VirtualMachineScaleSetVMProfile ، يتم استخدام متغير imageAndOsType دون التحقق من وجود قيمة خالية . ومع ذلك ، عند إنشاء VirtualMachineScaleSetStorageProfile ، يتم التحقق من هذا المتغير بالفعل باستخدام عامل الوصول المشروط ، ومرتين. الرمز يبدو غير آمن.

V3042 ممكن NullReferenceException. "؟" و "." تستخدم عوامل التشغيل للوصول إلى أعضاء كائن "ContContacts "الموجود 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(....); // <= } .... } 

كما هو الحال في التنفيذ العادي ، وكنتيجة لمعالجة الاستثناء ، يمكن أن يصبح متغيرContacts الموجود باطلاً ، وبعد ذلك سيستمر العمل. علاوة على ذلك ، يتم استخدام هذا المتغير بدون أي تحقق.

V3066 ترتيب غير صحيح محتمل للوسائط تم تمريره إلى طريقة "PersistSyncServerRegistration": 'storageSyncServiceUid' و '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); } .... } 

يشتبه المحلل في أن ترتيب تمرير الوسائط إلى أسلوب PersistSyncServerRegistration تم الخلط . إعلان الطريقة:

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

في الواقع ، هناك شيء ما مختلط هنا بالحجج رقم سبعة وثمانية. مطلوب التحقق من الرمز من قبل المؤلف.

V3077 لا يستخدم محدد خاصية "GetGuid" معلمة "القيمة" الخاصة به. RecoveryServicesBackupCmdletBase.cs 54 (RecoveryServices)

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

لا يستخدم setter المعلمة المنقولة. بدلاً من ذلك ، يحصلون على GUID جديد ويقومون بتعيينه إلى حقل _guid . أعتقد أن معظم القراء سيوافقون على أن هذا الرمز يبدو قبيحًا على الأقل. استخدام مثل هذا البناء ليس مناسبًا تمامًا: (لإعادة) تهيئة خاصية GetGuid ، يجب عليك تعيين قيمة مزيفة لها ، وهي غير واضحة تمامًا. لكن الأهم من ذلك كله أنني كنت مستمتعًا في الوقت الحالي كيف استخدم المؤلفون أنفسهم هذا البناء. يوجد مكان واحد فقط في الكود حيث يعمل GetGuid . ألقِ نظرة:

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

عظيم!

V3091 التحليل التجريبي. من الممكن وجود خطأ مطبعي داخل السلسلة الحرفية: "معرف مجموعة الإدارة". كلمة "معرف" مشبوهة. الثوابت. 36 (الموارد)

 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"; } 

أشار المحلل إلى وجود خطأ محتمل في السلسلة المخصصة لثابت GroupName . يستند الاستنتاج إلى تحليل تجريبي للمهام الأخرى مع مراعاة أسماء المتغيرات. أعتقد أنه في هذه الحالة يكون المحلل على حق ، ويجب أن تكون قيمة ثابت GroupName هي "اسم مجموعة الإدارة". من المحتمل أن يكون سبب الخطأ هو حقيقة أن قيمة ثابت GroupId تم نسخها ، لكنهم نسوا تغييرها.

خطأ مشابه آخر:

  • V3091 التحليل التجريبي. من الممكن وجود خطأ مطبعي داخل السلسلة الحرفية. كلمة "الاسم" مشبوهة. ParamHelpMsgs.cs 153 (خدمات الاسترداد)

V3093 The '|' المشغل بتقييم كل المعاملات. ربما ماس كهربائى "| يجب استخدام المشغل بدلاً من ذلك. PSKeyVaultCertificatePolicy.cs 114 (KeyVault)

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

هناك شك في حدوث خطأ ، كما يجب استخدام عامل التشغيل بين الشروط الأخيرة في كتلة if . الجواب الدقيق ، كما يحدث في كثير من الأحيان ، لا يمكن إلا أن يقدمه المطور.

V3095 تم استخدام عنصر "الشهادة" قبل أن يتم التحقق منه ضد قيمة خالية. خطوط التحقق: 41 ، 43. شهادةInfo.cs 41 (أتمتة)

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

الكلاسيكية. يتم استخدام الكائن أولاً ، وعندها فقط يتم التحقق من الارتباط لاغياً . نواجه مثل هذه الأخطاء في كثير من الأحيان . النظر في خطأ آخر مماثل.

V3095 تم استخدام العنصر 'clusterCred' قبل أن يتم التحقق منه ضد قيمة خالية. خطوط التحقق: 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) .... } 

واثنين من الأخطاء أكثر مماثلة:

  • V3095 تم استخدام العنصر '_profile' قبل أن يتم التحقق منه مقابل خالية. خطوط التحقق: 47 ، 49. RMProfileClient.cs 47 (الحسابات)
  • V3095 تم استخدام العنصر 'this.LoadBalancer.BackendAddressPools' قبل أن يتم التحقق منه ضد القيمة الخالية. خطوط التحقق: 56 ، 63. AddAzureRmLoadBalancerBackendAddressPoolConfigCommand.cs 56 (CognitiveServices)
  • بشكل عام ، في رمز Azure PowerShell ، قابلت الكثير من أخطاء V3095 . لكنهم جميعا متشابهون إلى حد ما ، لذلك لن أتناول هذا بمزيد من التفصيل.

V3125 تم استخدام كائن "startTime" بعد أن تم التحقق منه مقابل لاغٍ. خطوط التحقق: 1752 ، 1738. AutomationPSClientDSC.cs 1752 (التنفيذ التلقائي)

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

أيضا نوع شائع إلى حد ما من الخطأ. تم فحص متغير startTime لمعرفة القيمة عند أول استخدام. في المرة القادمة التي تستخدم فيها هذا ، لا تفعل ذلك. ولكن يمكن أن يكون أسوأ. نلقي نظرة على الثانية إذا كتلة. أعتقد أنه يجب ألا يكون هناك متغير startTime على الإطلاق . يشار إلى ذلك في كل من غياب التحقق من وجود قيمة قبل الاستخدام ، والسلسلة التي تم إنشاؤها للانتقال إلى الأسلوب Add . الجزء الأول من هذا الخط يذكر متغير آخر ( lastModifiedTime ).

V3125 تم استخدام العنصر ' firstPage ' بعد أن تم التحقق منه ضد قيمة خالية. خطوط التحقق: 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)) // <= { .... } .... } 

خطأ آخر واضح. يتم استخدام المتغير firstPage بشكل غير آمن ، على الرغم من أنه في وقت سابق من التعليمات البرمجية هناك استخدام لهذا المتغير مع فحص أولي للإلغاء .

كان V3125 في كود PowerShell Azure أكثر نجاحًا من V3095 الموصوف مسبقًا . كل منهم أيضا من نفس النوع. أعتقد أننا يمكن أن نقتصر على الاثنين اللذين درسناهما.

V3137 يتم تعيين متغير "apiVersionSetId" ولكن لا يتم استخدامه بحلول نهاية الوظيفة. 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)) // <= } } 

تقارير محلل أنه قد تمت تهيئة apiVersionSetId المتغير المحلي ، ولكن لم يتم استخدامه. غالبًا ما يشير هذا النمط إلى حدوث خطأ. أعتقد في هذه الحالة أن احتمال حدوث خطأ كبير ، نظرًا لأن اسم المتغير المحلي apiVersionSetId واسم الخاصية ApiVersionSetId يختلفان فقط في حالة الحرف الأول. ألقِ نظرة على الكود. بعد تهيئة apiVersionSetId (بطريقة أو بأخرى) ، يتم استخدام خاصية ApiVersionSetId فقط في الكود. مشبوهة جدا جدا.

V3137 يتم تعيين متغير "cacheId" ولكن لا يتم استخدامه بحلول نهاية الوظيفة. 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); // <= .... } 

الوضع ، تقريبا بالضبط تكرار النظر سابقا. لا يتم استخدام cacheId المتغير المحلي بعد التهيئة. بدلاً من ذلك ، يستخدمون خاصية باسم مشابه جدًا ، CacheId . لا أعرف ، ربما يكون هذا نمطًا بين مطوري Azure PowerShell. ولكن يبدو وكأنه خطأ.

V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. 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; } // <= } 

يتم الإعلان عن حقل partnerType كما يلي:

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

على عكس اسم الحل الذي تم اكتشاف هذا الخطأ فيه (LogicApp) ، لا أرى المنطق هنا. استخدام القيمة في أداة ضبط للتسجيل ليس بالأمر نادر الحدوث ، ولكن في هذه الحالة نتحدث عن فقدان القيمة الأصلية. يبدو غريبا. يحتوي الرمز على وصول للقراءة واحد إلى هذه الخاصية. ربما يجب عليك مرة أخرى طلب مشورة الخبراء. ربما لا أفهم شيئًا. الحقيقة هي أنني قابلت عددًا قليلاً من الأنماط نفسها بالضبط:

  • V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. NewAzureIntegrationAccountSchemaCommand.cs 79 (LogicApp)
  • V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. NewAzureIntegrationAccountSchemaCommand.cs 87 (LogicApp)
  • V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. UpdateAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)
  • V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. UpdateAzureIntegrationAccountSchemaCommand.cs 80 (LogicApp)
  • V3143 تتم إعادة كتابة المعلمة "value" داخل محدد خاصية ، ولا يتم استخدامها بعد ذلك. UpdateAzureIntegrationAccountSchemaCommand.cs 88 (LogicApp)

استنتاج


هذا كل الأخطاء التي تم العثور عليها في كود Azure PowerShell.المتحمسين والمهتمين فقط ، أقترح إجراء دراسة مستقلة للأخطاء في رمز هذا (أو أي مشروع آخر). ربما كنت قد فاتني شيء آخر مثير للاهتمام. للقيام بذلك ، ما عليك سوى تنزيل وتثبيت PVS-Studio .

شكرا لك على القراءة كود عديم الشفرة!



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بترجمة: سيرجي خرينوف. أزور PowerShell: غير مؤذية في الغالب .

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


All Articles