| File: Microsoft.NetCore.Analyzers\Security\DoNotInstallRootCert.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis; using Analyzer.Utilities.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; using Microsoft.CodeAnalysis.Operations; using Microsoft.NetCore.Analyzers.Security.Helpers; namespace Microsoft.NetCore.Analyzers.Security { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA5380: <inheritdoc cref="DefinitelyInstallRootCert"/> /// CA5381: <inheritdoc cref="MaybeInstallRootCert"/> /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DoNotInstallRootCert : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor DefinitelyInstallRootCertRule = SecurityHelpers.CreateDiagnosticDescriptor( "CA5380", nameof(DefinitelyInstallRootCert), nameof(DefinitelyInstallRootCertMessage), RuleLevel.Disabled, isPortedFxCopRule: false, isDataflowRule: true, isReportedAtCompilationEnd: true, descriptionResourceStringName: nameof(DoNotInstallRootCertDescription)); internal static readonly DiagnosticDescriptor MaybeInstallRootCertRule = SecurityHelpers.CreateDiagnosticDescriptor( "CA5381", nameof(MaybeInstallRootCert), nameof(MaybeInstallRootCertMessage), RuleLevel.Disabled, isPortedFxCopRule: false, isDataflowRule: true, isReportedAtCompilationEnd: true, descriptionResourceStringName: nameof(DoNotInstallRootCertDescription)); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create( DefinitelyInstallRootCertRule, MaybeInstallRootCertRule); private static readonly PropertyMapperCollection PropertyMappers = new( new PropertyMapper( "...dummy name", // There isn't *really* a property for what we're tracking; just the constructor argument. (PointsToAbstractValue v) => PropertySetAbstractValueKind.Unknown)); private static HazardousUsageEvaluationResult HazardousUsageCallback(IMethodSymbol methodSymbol, PropertySetAbstractValue propertySetAbstractValue) { return propertySetAbstractValue[0] switch { PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, _ => HazardousUsageEvaluationResult.Unflagged, }; } public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); // Security analyzer - analyze and report diagnostics on generated code. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); HazardousUsageEvaluatorCollection hazardousUsageEvaluators = new HazardousUsageEvaluatorCollection( new HazardousUsageEvaluator("Add", HazardousUsageCallback)); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartAnalysisContext) => { var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationStartAnalysisContext.Compilation); if (!wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemSecurityCryptographyX509CertificatesX509Store, out var x509TypeSymbol)) { return; } if (!wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemSecurityCryptographyX509CertificatesStoreName, out var storeNameTypeSymbol)) { return; } // If X509Store is initialized with Root store, then that instance is flagged. var constructorMapper = new ConstructorMapper( (IMethodSymbol constructorMethod, IReadOnlyList<ValueContentAbstractValue> argumentValueContentAbstractValues, IReadOnlyList<PointsToAbstractValue> argumentPointsToAbstractValues) => { var kind = PropertySetAbstractValueKind.Unflagged; if (!constructorMethod.Parameters.IsEmpty) { if (constructorMethod.Parameters[0].Type.Equals(storeNameTypeSymbol)) { kind = PropertySetCallbacks.EvaluateLiteralValues(argumentValueContentAbstractValues[0], o => o != null && o.Equals(6)); } else if (constructorMethod.Parameters[0].Type.SpecialType == SpecialType.System_String) { kind = PropertySetCallbacks.EvaluateLiteralValues( argumentValueContentAbstractValues[0], s => s != null && string.Equals(s.ToString(), "root", StringComparison.OrdinalIgnoreCase)); } } return PropertySetAbstractValue.GetInstance(kind); }); var rootOperationsNeedingAnalysis = PooledHashSet<(IOperation, ISymbol)>.GetInstance(); compilationStartAnalysisContext.RegisterOperationBlockStartAction( (OperationBlockStartAnalysisContext operationBlockStartAnalysisContext) => { var owningSymbol = operationBlockStartAnalysisContext.OwningSymbol; // TODO: Handle case when exactly one of the below rules is configured to skip analysis. if (operationBlockStartAnalysisContext.Options.IsConfiguredToSkipAnalysis(DefinitelyInstallRootCertRule, owningSymbol, operationBlockStartAnalysisContext.Compilation) && operationBlockStartAnalysisContext.Options.IsConfiguredToSkipAnalysis(MaybeInstallRootCertRule, owningSymbol, operationBlockStartAnalysisContext.Compilation)) { return; } operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { var invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (x509TypeSymbol.Equals(invocationOperation.Instance?.Type) && invocationOperation.TargetMethod.Name == "Add") { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add((invocationOperation.GetRoot(), operationAnalysisContext.ContainingSymbol)); } } }, OperationKind.Invocation); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { var argumentOperation = (IArgumentOperation)operationAnalysisContext.Operation; if (argumentOperation.Parameter != null && x509TypeSymbol.Equals(argumentOperation.Parameter.Type)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add((argumentOperation.GetRoot(), operationAnalysisContext.ContainingSymbol)); } } }, OperationKind.Argument); }); compilationStartAnalysisContext.RegisterCompilationEndAction( (CompilationAnalysisContext compilationAnalysisContext) => { PooledDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>? allResults = null; try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } allResults = PropertySetAnalysis.BatchGetOrComputeHazardousUsages( compilationAnalysisContext.Compilation, rootOperationsNeedingAnalysis, compilationAnalysisContext.Options, WellKnownTypeNames.SystemSecurityCryptographyX509CertificatesX509Store, constructorMapper, PropertyMappers, hazardousUsageEvaluators, InterproceduralAnalysisConfiguration.Create( compilationAnalysisContext.Options, SupportedDiagnostics, rootOperationsNeedingAnalysis.First().Item1, compilationAnalysisContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive)); } if (allResults == null) { return; } foreach (KeyValuePair<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> kvp in allResults) { DiagnosticDescriptor descriptor; switch (kvp.Value) { case HazardousUsageEvaluationResult.Flagged: descriptor = DefinitelyInstallRootCertRule; break; case HazardousUsageEvaluationResult.MaybeFlagged: descriptor = MaybeInstallRootCertRule; break; default: Debug.Fail($"Unhandled result value {kvp.Value}"); continue; } RoslynDebug.Assert(kvp.Key.Method != null); // HazardousUsageEvaluations only for invocations. compilationAnalysisContext.ReportDiagnostic( Diagnostic.Create( descriptor, kvp.Key.Location, kvp.Key.Method.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat))); } } finally { rootOperationsNeedingAnalysis.Free(compilationAnalysisContext.CancellationToken); allResults?.Free(compilationAnalysisContext.CancellationToken); } }); }); } } }