protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) { base.PostProcessArgument(operation, isEscaped); if (isEscaped) { // Discover if a disposable object is being passed into the creation method for this new disposable object // and if the new disposable object assumes ownership of that passed in disposable object. if (IsDisposeOwnershipTransfer()) { var pointsToValue = GetPointsToAbstractValue(operation.Value); HandlePossibleEscapingOperation(operation, pointsToValue.Locations); return; } else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && operation.Parameter.RefKind == RefKind.Out && operation.Parent is IInvocationOperation invocation && invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean) { // Case 1: // // Assume 'obj' is not a valid object on the 'else' path. // if (TryXXX(out IDisposable obj)) // { // obj.Dispose(); // } // // return; // Case 2: // if (!TryXXX(out IDisposable obj)) // { // return; // Assume 'obj' is not a valid object on this path. // } // // obj.Dispose(); HandlePossibleInvalidatingOperation(operation); return; } } // Ref or out argument values from callee might be escaped by assigning to field. if (operation.Parameter.RefKind is RefKind.Out or RefKind.Ref) { HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation)); } return; // Local functions. bool IsDisposeOwnershipTransfer() { if (operation.Parameter.RefKind == RefKind.Out) { // Out arguments are always owned by the caller. return(false); } return(operation.Parent switch { IObjectCreationOperation _ => DisposeOwnershipTransferAtConstructor || DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), IInvocationOperation invocation => DisposeOwnershipTransferAtMethodCall || IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), _ => false, }); }
protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation) { if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation, out var actualOperation, out var constraintExpression)) { return; } if (actualOperation is null) { return; } var actualType = AssertHelper.GetUnwrappedActualType(actualOperation); if (actualType is null) { return; } foreach (var constraintPartExpression in constraintExpression.ConstraintParts) { if (constraintPartExpression.HasIncompatiblePrefixes() || HasCustomComparer(constraintPartExpression) || constraintPartExpression.HasUnknownExpressions()) { continue; } var constraintMethod = constraintPartExpression.GetConstraintMethod(); if (constraintMethod is null) { continue; } if (!SupportedConstraints.Contains(constraintMethod.Name)) { continue; } var expectedOperation = constraintPartExpression.GetExpectedArgument(); if (expectedOperation is null) { continue; } var expectedType = expectedOperation.Type; if (expectedType is null) { continue; } if (actualType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { actualType = ((INamedTypeSymbol)actualType).TypeArguments[0]; } if (expectedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { expectedType = ((INamedTypeSymbol)expectedType).TypeArguments[0]; } if (actualType.SpecialType == SpecialType.System_Object || expectedType.SpecialType == SpecialType.System_Object) { // An instance of object might not implement IComparable resulting in runtime errors. context.ReportDiagnostic(Diagnostic.Create( comparableOnObjectDescriptor, expectedOperation.Syntax.GetLocation(), constraintMethod.Name)); } else if (!CanCompare(actualType, expectedType, context.Compilation)) { context.ReportDiagnostic(Diagnostic.Create( comparableTypesDescriptor, expectedOperation.Syntax.GetLocation(), constraintMethod.Name)); } } }
protected override void Analyze(OperationAnalysisContext context, IInvocationOperation invocationOperation, IMethodSymbol method) { if (method.Parameters.Length != 2 || !method.Parameters[0].Type.SpecialType.Equals(SpecialType.System_Int32) || !method.Parameters[1].Type.SpecialType.Equals(SpecialType.System_Int32)) { return; } var sizeOperation = invocationOperation.Arguments.FirstOrDefault(arg => arg.Parameter.Equals(method.Parameters[0]))?.Value; var sizeValue = sizeOperation?.ConstantValue ?? default; if (!sizeValue.HasValue) { return; } // Make sure the first parameter really is an int before checking its value. Could for example be a char. if (!(sizeValue.Value is int size)) { return; } if (size < 0 || size > 1 || size == 1 && method.Name != "Equal") { return; } var otherArgument = invocationOperation.Arguments.FirstOrDefault(arg => !arg.Parameter.Equals(method.Parameters[0])); ISymbol symbol = otherArgument?.Value switch { IInvocationOperation o => o.TargetMethod, IPropertyReferenceOperation p => p.Property, _ => null, }; if (symbol == null) { return; } if (IsCollectionsWithExceptionThrowingGetEnumeratorMethod(symbol) || !IsWellKnownSizeMethod(symbol) && !IsICollectionCountProperty(context, symbol) && !IsICollectionOfTCountProperty(context, symbol) && !IsIReadOnlyCollectionOfTCountProperty(context, symbol)) { return; } var builder = ImmutableDictionary.CreateBuilder <string, string>(); builder[MethodName] = method.Name; builder[SizeValue] = size.ToString(); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat.CSharpShortErrorMessageFormat.WithParameterOptions(SymbolDisplayParameterOptions.None).WithGenericsOptions(SymbolDisplayGenericsOptions.None)))); }
/// <summary> /// Checks if the given invocation has an argument that is an empty string. /// </summary> private static bool HasAnEmptyStringArgument(IInvocationOperation invocation) { return(invocation.Arguments.Any(arg => IsEmptyString(arg.Value))); }
private static bool HasNonImplicitInstance(IInvocationOperation invocation) => invocation.Instance != null && !invocation.Instance.IsImplicit;
private static Task <Document> RemoveRedundantCall(Document document, SyntaxNode root, SyntaxNode invocationNode, IInvocationOperation invocationOperation) { var instance = GetInstance(invocationOperation).WithTriviaFrom(invocationNode); var newRoot = root.ReplaceNode(invocationNode, instance); var newDocument = document.WithSyntaxRoot(newRoot); return(Task.FromResult(newDocument)); }
public sealed override void Initialize(AnalysisContext context) { ImmutableHashSet <string> cachedDeserializationMethodNames = this.DeserializationMethodNames; Debug.Assert(!String.IsNullOrWhiteSpace(this.DeserializerTypeMetadataName)); Debug.Assert(!String.IsNullOrWhiteSpace(this.SerializationBinderPropertyMetadataName)); Debug.Assert(cachedDeserializationMethodNames != null); Debug.Assert(!cachedDeserializationMethodNames.IsEmpty); Debug.Assert(this.BinderDefinitelyNotSetDescriptor != null); Debug.Assert(this.BinderMaybeNotSetDescriptor != null); context.EnableConcurrentExecution(); // Security analyzer - analyze and report diagnostics on generated code. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); // For PropertySetAnalysis dataflow analysis. PropertyMapperCollection propertyMappers = new PropertyMapperCollection( new PropertyMapper( this.SerializationBinderPropertyMetadataName, (NullAbstractValue nullAbstractValue) => { // A null SerializationBinder is what we want to flag as hazardous. switch (nullAbstractValue) { case NullAbstractValue.Null: return(PropertySetAbstractValueKind.Flagged); case NullAbstractValue.NotNull: return(PropertySetAbstractValueKind.Unflagged); default: return(PropertySetAbstractValueKind.MaybeFlagged); } })); HazardousUsageEvaluatorCollection hazardousUsageEvaluators = new HazardousUsageEvaluatorCollection( cachedDeserializationMethodNames.Select( methodName => new HazardousUsageEvaluator(methodName, DoNotUseInsecureDeserializerWithoutBinderBase.HazardousIfNull))); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartAnalysisContext) => { WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationStartAnalysisContext.Compilation); if (!wellKnownTypeProvider.TryGetTypeByMetadataName( this.DeserializerTypeMetadataName, out INamedTypeSymbol deserializerTypeSymbol)) { return; } compilationStartAnalysisContext.RegisterOperationBlockStartAction( (OperationBlockStartAnalysisContext operationBlockStartAnalysisContext) => { PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance(); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (invocationOperation.Instance?.Type == deserializerTypeSymbol && cachedDeserializationMethodNames.Contains(invocationOperation.TargetMethod.Name)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.Invocation); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IMethodReferenceOperation methodReferenceOperation = (IMethodReferenceOperation)operationAnalysisContext.Operation; if (methodReferenceOperation.Instance?.Type == deserializerTypeSymbol && cachedDeserializationMethodNames.Contains( methodReferenceOperation.Method.MetadataName)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.MethodReference); operationBlockStartAnalysisContext.RegisterOperationBlockEndAction( (OperationBlockAnalysisContext operationBlockAnalysisContext) => { PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> allResults = null; try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } // Only instantiated if there are any results to report. List <ControlFlowGraph> cfgs = new List <ControlFlowGraph>(); var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( operationBlockAnalysisContext.Options, SupportedDiagnostics, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.None, cancellationToken: operationBlockAnalysisContext.CancellationToken, defaultMaxInterproceduralMethodCallChain: 1); // By default, we only want to track method calls one level down. foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { ImmutableDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> dfaResult = PropertySetAnalysis.GetOrComputeHazardousUsages( rootOperation.GetEnclosingControlFlowGraph(), operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, this.DeserializerTypeMetadataName, DoNotUseInsecureDeserializerWithoutBinderBase.ConstructorMapper, propertyMappers, hazardousUsageEvaluators, interproceduralAnalysisConfig); if (dfaResult.IsEmpty) { continue; } if (allResults == null) { allResults = PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> .GetInstance(); } foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp in dfaResult) { allResults.Add(kvp.Key, kvp.Value); } } } if (allResults == null) { return; } foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp in allResults) { DiagnosticDescriptor descriptor; switch (kvp.Value) { case HazardousUsageEvaluationResult.Flagged: descriptor = this.BinderDefinitelyNotSetDescriptor; break; case HazardousUsageEvaluationResult.MaybeFlagged: descriptor = this.BinderMaybeNotSetDescriptor; break; default: Debug.Fail($"Unhandled result value {kvp.Value}"); continue; } operationBlockAnalysisContext.ReportDiagnostic( Diagnostic.Create( descriptor, kvp.Key.Location, kvp.Key.Method.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat))); } } finally { rootOperationsNeedingAnalysis.Free(); allResults?.Free(); } }); }); }); }
private static void CombineWhereWithNextMethod(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols) { if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal)) { // Cannot replace Where when using Func<TSource,int,bool> if (operation.TargetMethod.Parameters.Length != 2) { return; } var type = operation.TargetMethod.Parameters[1].Type as INamedTypeSymbol; if (type == null) { return; } if (type.TypeArguments.Length == 3) { return; } // Check parent methods var parent = GetParentLinqOperation(operation); if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType)) { if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.First), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.FirstOrDefault), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Last), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.LastOrDefault), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Single), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.SingleOrDefault), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Any), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.LongCount), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal)) { var properties = CreateProperties(OptimizeLinqUsageData.CombineWhereWithNextMethod) .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("MethodName", parent.TargetMethod.Name); context.ReportDiagnostic(s_combineLinqMethodsRule, properties, parent, operation.TargetMethod.Name, parent.TargetMethod.Name); } } } }
private static void RemoveTwoConsecutiveOrderBy(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols) { if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) || string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal) || string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ThenBy), StringComparison.Ordinal) || string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ThenByDescending), StringComparison.Ordinal)) { var parent = GetParentLinqOperation(operation); if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType)) { if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) || string.Equals(parent.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal)) { var expectedMethodName = parent.TargetMethod.Name.Replace("OrderBy", "ThenBy"); var properties = CreateProperties(OptimizeLinqUsageData.DuplicatedOrderBy) .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("ExpectedMethodName", expectedMethodName) .Add("MethodName", parent.TargetMethod.Name); context.ReportDiagnostic(s_duplicateOrderByMethodsRule, properties, parent, operation.TargetMethod.Name, expectedMethodName); } } } }
private static void UseFindInsteadOfFirstOrDefault(OperationAnalysisContext context, IInvocationOperation operation) { if (!string.Equals(operation.TargetMethod.Name, nameof(Enumerable.FirstOrDefault), StringComparison.Ordinal)) { return; } if (operation.Arguments.Length != 2) { return; } var listSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1"); if (GetActualType(operation.Arguments[0]).OriginalDefinition.IsEqualTo(listSymbol)) { var properties = CreateProperties(OptimizeLinqUsageData.UseFindMethod); context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Find()", operation.TargetMethod.Name); } }
private static void UseIndexerInsteadOfElementAt(OperationAnalysisContext context, IInvocationOperation operation) { ImmutableDictionary <string, string> properties = default; var argCount = -1; if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.ElementAt), StringComparison.Ordinal)) { properties = CreateProperties(OptimizeLinqUsageData.UseIndexer); argCount = 2; } else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.First), StringComparison.Ordinal)) { properties = CreateProperties(OptimizeLinqUsageData.UseIndexerFirst); argCount = 1; } else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Last), StringComparison.Ordinal)) { properties = CreateProperties(OptimizeLinqUsageData.UseIndexerLast); argCount = 1; } if (argCount < 0) { return; } if (operation.Arguments.Length != argCount) { return; } var listSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IList`1"); var readOnlyListSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyList`1"); if (listSymbol == null && readOnlyListSymbol == null) { return; } var actualType = GetActualType(operation.Arguments[0]); if (actualType.AllInterfaces.Any(i => i.OriginalDefinition.Equals(listSymbol) || i.OriginalDefinition.Equals(readOnlyListSymbol))) { context.ReportDiagnostic(s_listMethodsRule, properties, operation, "[]", operation.TargetMethod.Name); } }
private static void UseCountPropertyInsteadOfMethod(OperationAnalysisContext context, IInvocationOperation operation) { if (operation.Arguments.Length != 1) { return; } if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal)) { var collectionOfTSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.ICollection`1"); var readOnlyCollectionOfTSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyCollection`1"); if (collectionOfTSymbol == null && readOnlyCollectionOfTSymbol == null) { return; } var actualType = GetActualType(operation.Arguments[0]); if (actualType.TypeKind == TypeKind.Array) { var properties = CreateProperties(OptimizeLinqUsageData.UseLengthProperty); context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Length", operation.TargetMethod.Name); return; } if (actualType.AllInterfaces.Any(i => i.OriginalDefinition.Equals(collectionOfTSymbol) || i.OriginalDefinition.Equals(readOnlyCollectionOfTSymbol))) { // Ensure the Count property is not an explicit implementation var count = actualType.GetMembers("Count").OfType <IPropertySymbol>().FirstOrDefault(m => m.ExplicitInterfaceImplementations.Length == 0); if (count != null) { var properties = CreateProperties(OptimizeLinqUsageData.UseCountProperty); context.ReportDiagnostic(s_listMethodsRule, properties, operation, "Count", operation.TargetMethod.Name); return; } } } else if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.LongCount), StringComparison.Ordinal)) { var actualType = GetActualType(operation.Arguments[0]); if (actualType.TypeKind == TypeKind.Array) { var properties = CreateProperties(OptimizeLinqUsageData.UseLongLengthProperty); context.ReportDiagnostic(s_listMethodsRule, properties, operation, "LongLength", operation.TargetMethod.Name); } } }
private static void WhereShouldBeBeforeOrderBy(OperationAnalysisContext context, IInvocationOperation operation, List <INamedTypeSymbol> enumerableSymbols) { if (string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderBy), StringComparison.Ordinal) || string.Equals(operation.TargetMethod.Name, nameof(Enumerable.OrderByDescending), StringComparison.Ordinal)) { var parent = GetParentLinqOperation(operation); if (parent != null && enumerableSymbols.Contains(parent.TargetMethod.ContainingType)) { if (string.Equals(parent.TargetMethod.Name, nameof(Enumerable.Where), StringComparison.Ordinal)) { var properties = CreateProperties(OptimizeLinqUsageData.CombineWhereWithNextMethod) .Add("FirstOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("FirstOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationStart", parent.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("LastOperationLength", parent.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("MethodName", parent.TargetMethod.Name); context.ReportDiagnostic(s_optimizeWhereAndOrderByRule, properties, parent, operation.TargetMethod.Name); } } } }
/// <summary> /// Gets the value of precision used in Equal/NotEqual invocation or <c>null</c> if cannot be obtained. /// This has the semantic of: 1. Ensure the analysis applies, 2. Get data for further analysis. /// </summary> /// <param name="invocation"></param> private static (IArgumentOperation precisionArgument, int value)? GetNumericLiteralValue(IInvocationOperation invocation) { if (invocation.Arguments.Length != 3) { return(null); } var precisionParameter = invocation.TargetMethod.Parameters[2]; var precisionArgument = invocation.Arguments.First(arg => arg.Parameter.Equals(precisionParameter)); if (precisionArgument is null) { return(null); } var constantValue = precisionArgument.Value.ConstantValue; if (!constantValue.HasValue || !(constantValue.Value is int value)) { return(null); } return(precisionArgument, value); }
public override DisposeAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction(IInvocationOperation operation, object argument) { var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(operation, argument); var disposeMethodKind = operation.TargetMethod.GetDisposeMethodKind(WellKnownTypeProvider.IDisposable); switch (disposeMethodKind) { case DisposeMethodKind.Dispose: case DisposeMethodKind.DisposeBool: HandleDisposingOperation(operation, operation.Instance); break; case DisposeMethodKind.Close: // FxCop compat: Calling "this.Close" shouldn't count as disposing the object within the implementation of Dispose. if (operation.Instance?.Kind != OperationKind.InstanceReference) { goto case DisposeMethodKind.Dispose; } break; default: // FxCop compat: Catches things like static calls to File.Open() and Create() if (IsDisposableCreationSpecialCase(operation)) { var instanceLocation = GetPointsToAbstractValue(operation); return(HandleInstanceCreation(operation.Type, instanceLocation, value)); } else if (operation.Arguments.Length > 0 && operation.TargetMethod.IsCollectionAddMethod(WellKnownTypeProvider.CollectionTypes)) { // FxCop compat: The object added to a collection is considered escaped. var lastArgument = operation.Arguments[operation.Arguments.Length - 1]; HandlePossibleEscapingOperation(operation, lastArgument.Value); } break; } return(value); }
private static void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOperation operation, IReadOnlyCollection <INamedTypeSymbol> enumerableSymbols) { if (!string.Equals(operation.TargetMethod.Name, nameof(Enumerable.Count), StringComparison.Ordinal)) { return; } var binaryOperation = GetParentBinaryOperation(operation, out var countOperand); if (binaryOperation == null) { return; } if (!IsSupportedOperator(binaryOperation.OperatorKind)) { return; } if (!binaryOperation.LeftOperand.Type.IsInt32() || !binaryOperation.RightOperand.Type.IsInt32()) { return; } var opKind = NormalizeOperator(); var otherOperand = binaryOperation.LeftOperand == countOperand ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (otherOperand == null) { return; } string message = null; var properties = ImmutableDictionary <string, string> .Empty; if (otherOperand.ConstantValue.HasValue && otherOperand.ConstantValue.Value is int value) { switch (opKind) { case BinaryOperatorKind.Equals: if (value < 0) { // expr.Count() == -1 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 0) { // expr.Count() == 0 message = "Replace 'Count() == 0' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() == 1 if (!HasTake()) { message = Invariant($"Replace 'Count() == {value}' with 'Take({value + 1}).Count() == {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } break; case BinaryOperatorKind.NotEquals: if (value < 0) { // expr.Count() != -1 is always true message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 0) { // expr.Count() != 0 message = "Replace 'Count() != 0' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() != 1 if (!HasTake()) { message = Invariant($"Replace 'Count() != {value}' with 'Take({value + 1}).Count() != {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } break; case BinaryOperatorKind.LessThan: if (value <= 0) { // expr.Count() < 0 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 1) { // expr.Count() < 1 ==> expr.Count() == 0 message = "Replace 'Count() < 1' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() < 10 message = Invariant($"Replace 'Count() < {value}' with 'Skip({value - 1}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny) .Add("SkipMinusOne", null); } break; case BinaryOperatorKind.LessThanOrEqual: if (value < 0) { // expr.Count() <= -1 message = "Expression is always false"; properties = CreateProperties(OptimizeLinqUsageData.UseFalse); } else if (value == 0) { // expr.Count() <= 0 message = "Replace 'Count() <= 0' with 'Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseNotAny); } else { // expr.Count() < 10 message = Invariant($"Replace 'Count() <= {value}' with 'Skip({value}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny); } break; case BinaryOperatorKind.GreaterThan: if (value < 0) { // expr.Count() > -1 message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 0) { // expr.Count() > 0 message = "Replace 'Count() > 0' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() > 1 message = Invariant($"Replace 'Count() > {value}' with 'Skip({value}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny); } break; case BinaryOperatorKind.GreaterThanOrEqual: if (value <= 0) { // expr.Count() >= 0 message = "Expression is always true"; properties = CreateProperties(OptimizeLinqUsageData.UseTrue); } else if (value == 1) { // expr.Count() >= 1 message = "Replace 'Count() >= 1' with 'Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseAny); } else { // expr.Count() >= 2 message = Invariant($"Replace 'Count() >= {value}' with 'Skip({value - 1}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny) .Add("SkipMinusOne", null); } break; } } else { switch (opKind) { case BinaryOperatorKind.Equals: // expr.Count() == 1 if (!HasTake()) { message = "Replace 'Count() == n' with 'Take(n + 1).Count() == n'"; properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } break; case BinaryOperatorKind.NotEquals: // expr.Count() != 1 if (!HasTake()) { message = "Replace 'Count() != n' with 'Take(n + 1).Count() != n'"; properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } break; case BinaryOperatorKind.LessThan: // expr.Count() < 10 message = "Replace 'Count() < n' with 'Skip(n - 1).Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny) .Add("SkipMinusOne", null); break; case BinaryOperatorKind.LessThanOrEqual: // expr.Count() <= 10 message = "Replace 'Count() <= n' with 'Skip(n).Any() == false'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny); break; case BinaryOperatorKind.GreaterThan: // expr.Count() > 1 message = "Replace 'Count() > n' with 'Skip(n).Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny); break; case BinaryOperatorKind.GreaterThanOrEqual: // expr.Count() >= 2 message = "Replace 'Count() >= n' with 'Skip(n - 1).Any()'"; properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny) .Add("SkipMinusOne", null); break; } } if (message != null) { properties = properties .Add("OperandOperationStart", otherOperand.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("OperandOperationLength", otherOperand.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)) .Add("CountOperationStart", operation.Syntax.Span.Start.ToString(CultureInfo.InvariantCulture)) .Add("CountOperationLength", operation.Syntax.Span.Length.ToString(CultureInfo.InvariantCulture)); context.ReportDiagnostic(s_optimizeCountRule, properties, binaryOperation, message); }
private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements( IInvocationOperation operation, ContractMethodNames contractMethod, SemanticModel semanticModel, CancellationToken token) { var invocationExpression = operation.Syntax; // Getting the original predicate. var predicateArgumentOperation = operation.Arguments[0]; var predicateArgument = (ArgumentSyntax)predicateArgumentOperation.Syntax; ArgumentSyntax?extraForAllArgument = null; int messageArgumentIndex = 1; // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty if (contractMethod.IsNullCheck()) { predicateArgument = Argument( // Changing NotNull(x) to x != null BinaryExpression( SyntaxKind.NotEqualsExpression, predicateArgument.Expression, LiteralExpression(SyntaxKind.NullLiteralExpression))) .NormalizeWhitespace(); } else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace()) { var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace); // Targeting a full framework can cause an issue for null-ness analysis // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes. // It means that the compiler can't recognize that the following code is correct and still emit the warning: // if (!string.IsNullOrEmpty(str)) return str.Length; predicateArgument = Argument( PrefixUnaryExpression( SyntaxKind.LogicalNotExpression, InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.StringKeyword)), IdentifierName(stringMethodName))) .WithArgumentList( ArgumentList(SingletonSeparatedList(predicateArgument)))) ).NormalizeWhitespace(); } else if (contractMethod.IsForAll()) { extraForAllArgument = (ArgumentSyntax)operation.Arguments[1].Syntax; messageArgumentIndex++; } // Detecting the following case: // if (predicate is false) {Contract.Assert(false, complicatedMessage);} var sourceNode = invocationExpression.Parent; if (predicateArgumentOperation.Value is ILiteralOperation lo && lo.ConstantValue.HasValue && lo.ConstantValue.Value.Equals(false)) { // this is Assert(false) case. if (operation.Parent?.Parent?.Parent is IConditionalOperation conditional && conditional.WhenFalse == null && conditional.WhenTrue.Children.Count() == 1) { // The contract is inside the if block with a single statement. sourceNode = conditional.Syntax; var negatedCondition = (ExpressionSyntax?)SyntaxGeneratorExtensions.Negate(conditional.Condition.Syntax, semanticModel, token); if (negatedCondition != null) { predicateArgument = Argument(negatedCondition); } } } var originalMessageArgument = operation.Arguments[messageArgumentIndex]; Func <string> nonDefaultArgument = () => contractMethod.IsForAll() ? operation.Arguments[1].Syntax.ToFullString() : predicateArgument.ToFullString(); // Using an original message if provided. // Otherwise using a predicate as the new message. var messageArgument = originalMessageArgument.IsImplicit == false ? (ArgumentSyntax)originalMessageArgument.Syntax : Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(nonDefaultArgument()))); var arguments = new SeparatedSyntaxList <ArgumentSyntax>() .Add(predicateArgument) .AddIfNotNull(extraForAllArgument); // Generating Contract.Check(predicate)?.Requires/Assert(message) var targetMethodName = GetTargetMethod(contractMethod); var checkMethodName = GetCheckMethod(contractMethod); var contractCall = ExpressionStatement( ConditionalAccessExpression( // Contract.Requires( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(FluentContractNames.ContractClassName), IdentifierName(checkMethodName))) // (predicate) .WithArgumentList( ArgumentList(arguments)), // ?.IsTrue(message) InvocationExpression( MemberBindingExpression( IdentifierName(targetMethodName))) .WithArgumentList( ArgumentList( SingletonSeparatedList(messageArgument))) ) ); var trivia = sourceNode.GetLeadingTrivia(); var finalNode = contractCall.WithLeadingTrivia(trivia); return(sourceNode, finalNode); }
static void CaptureHashCreateInvocationOperation(DataCollector dataCollector, IInvocationOperation hashCreateInvocation) { if (TryGetVariableInitializerOperation(hashCreateInvocation.Parent, out var variableInitializerOperation)) { var ownerType = hashCreateInvocation.TargetMethod.ContainingType; CaptureVariableDeclaratorOperation(dataCollector, ownerType, variableInitializerOperation); } }
private static SyntaxNode GetInstance(IInvocationOperation invocationOperation) { return(invocationOperation.TargetMethod.IsExtensionMethod && invocationOperation.Language != LanguageNames.VisualBasic ? invocationOperation.Arguments[0].Value.Syntax : invocationOperation.Instance.Syntax); }
static void CaptureOrReportComputeHashInvocationOperation(OperationAnalysisContext context, MethodHelper methodHelper, DataCollector dataCollector, IInvocationOperation computeHashInvocation, ComputeType computeType) { switch (computeHashInvocation.Instance) { case ILocalReferenceOperation: dataCollector.CollectComputeHashInvocation(computeHashInvocation, computeType); break; case IInvocationOperation chainedInvocationOperation when methodHelper.IsHashCreateMethod(chainedInvocationOperation): ReportChainedComputeHashInvocationOperation(chainedInvocationOperation.TargetMethod.ContainingType); break; case IObjectCreationOperation chainObjectCreationOperation when methodHelper.IsObjectCreationInheritingHashAlgorithm(chainObjectCreationOperation): ReportChainedComputeHashInvocationOperation(chainObjectCreationOperation.Type); break; } void ReportChainedComputeHashInvocationOperation(ITypeSymbol originalHashType) { if (!methodHelper.TryGetHashDataMethod(originalHashType, computeType, out var staticHashMethod)) { return; } var diagnostics = CreateDiagnostics(computeHashInvocation, staticHashMethod.ContainingType, computeType); context.ReportDiagnostic(diagnostics); } }
public override void Initialize(AnalysisContext analysisContext) { analysisContext.EnableConcurrentExecution(); analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); analysisContext.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol expectedExceptionType = WellKnownTypes.ExpectedException(compilationContext.Compilation); INamedTypeSymbol nunitAssertType = WellKnownTypes.NunitAssert(compilationContext.Compilation); INamedTypeSymbol xunitAssertType = WellKnownTypes.XunitAssert(compilationContext.Compilation); compilationContext.RegisterOperationBlockStartAction(osContext => { if (!(osContext.OwningSymbol is IMethodSymbol method)) { return; } osContext.RegisterOperationAction(opContext => { IOperation expression = ((IExpressionStatementOperation)opContext.Operation).Operation; DiagnosticDescriptor rule = null; string targetMethodName = null; switch (expression.Kind) { case OperationKind.ObjectCreation: IMethodSymbol ctor = ((IObjectCreationOperation)expression).Constructor; if (ctor != null) { rule = ObjectCreationRule; targetMethodName = ctor.ContainingType.Name; } break; case OperationKind.Invocation: IInvocationOperation invocationExpression = ((IInvocationOperation)expression); IMethodSymbol targetMethod = invocationExpression.TargetMethod; if (targetMethod == null) { break; } if (IsStringCreatingMethod(targetMethod)) { rule = StringCreationRule; } else if (IsTryParseMethod(targetMethod)) { rule = TryParseRule; } else if (IsHResultOrErrorCodeReturningMethod(targetMethod)) { rule = HResultOrErrorCodeRule; } else if (IsPureMethod(targetMethod, opContext.Compilation)) { rule = PureMethodRule; } targetMethodName = targetMethod.Name; break; } if (rule != null) { if (ShouldSkipAnalyzing(opContext, expectedExceptionType, xunitAssertType, nunitAssertType)) { return; } Diagnostic diagnostic = Diagnostic.Create(rule, expression.Syntax.GetLocation(), method.Name, targetMethodName); opContext.ReportDiagnostic(diagnostic); } }, OperationKind.ExpressionStatement); }); }); }
private bool CheckForNestedStringFormat(OperationAnalysisContext operationContext, IInvocationOperation target, IInvocationOperation argument) { var targetMethod = argument.TargetMethod; if (!IsStringFormatMethod(targetMethod, out _, out _)) { return(false); } operationContext.ReportDiagnostic(Diagnostic.Create(NestedRule, argument.Syntax.GetLocation(), targetMethod.ToDisplayString(), target.TargetMethod.ToDisplayString())); return(true); }
public virtual void VisitInvocation(IInvocationOperation operation) { DefaultVisit(operation); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { Compilation compilation = compilationContext.Compilation; TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilation); TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind); if (sourceInfoSymbolMap.IsEmpty) { return; } TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; AnalyzerOptions options = operationBlockStartContext.Options; CancellationToken cancellationToken = operationBlockStartContext.CancellationToken; if (owningSymbol.IsConfiguredToSkipAnalysis(options, TaintedDataEnteringSinkDescriptor, compilation, cancellationToken)) { return; } WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create( options, SupportedDiagnostics, owningSymbol, operationBlockStartContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: cancellationToken); Lazy <ControlFlowGraph?> controlFlowGraphFactory = new Lazy <ControlFlowGraph?>( () => operationBlockStartContext.OperationBlocks.GetControlFlowGraph()); Lazy <PointsToAnalysisResult?> pointsToFactory = new Lazy <PointsToAnalysisResult?>( () => { if (controlFlowGraphFactory.Value == null) { return(null); } return(PointsToAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, interproceduralAnalysisConfiguration, interproceduralAnalysisPredicateOpt: null)); }); Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)> valueContentFactory = new Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)>( () => { if (controlFlowGraphFactory.Value == null) { return(null, null); } ValueContentAnalysisResult?valuecontentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, interproceduralAnalysisConfiguration, out _, out PointsToAnalysisResult? p); return(p, valuecontentAnalysisResult); }); PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance(); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(propertyReferenceOperation.GetRoot()); } } }, OperationKind.PropertyReference); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceMethod( invocationOperation.TargetMethod, invocationOperation.Arguments, pointsToFactory, valueContentFactory, out _)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(invocationOperation.GetRoot()); } } }, OperationKind.Invocation); if (TaintedDataConfig.HasTaintArraySource(SinkKind)) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IArrayInitializerOperation arrayInitializerOperation = (IArrayInitializerOperation)operationAnalysisContext.Operation; if (arrayInitializerOperation.GetAncestor <IArrayCreationOperation>(OperationKind.ArrayCreation)?.Type is IArrayTypeSymbol arrayTypeSymbol && sourceInfoSymbolMap.IsSourceConstantArrayOfType(arrayTypeSymbol)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.ArrayInitializer); } operationBlockStartContext.RegisterOperationBlockEndAction( operationBlockAnalysisContext => { try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } if (controlFlowGraphFactory.Value == null) { return; } foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { TaintedDataAnalysisResult?taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, operationBlockAnalysisContext.Options, TaintedDataEnteringSinkDescriptor, sourceInfoSymbolMap, taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind), sinkInfoSymbolMap, operationBlockAnalysisContext.CancellationToken); if (taintedDataAnalysisResult == null) { return; } foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks) { if (!sourceSink.SinkKinds.Contains(this.SinkKind)) { continue; } foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins) { // Something like: // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'. Diagnostic diagnostic = Diagnostic.Create( this.TaintedDataEnteringSinkDescriptor, sourceSink.Sink.Location, additionalLocations: new Location[] { sourceOrigin.Location }, messageArgs: new object[] { sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationBlockAnalysisContext.ReportDiagnostic(diagnostic); } } } } } finally { rootOperationsNeedingAnalysis.Free(); } }); }); }); }
public sealed override void Initialize(AnalysisContext context) { ImmutableHashSet <string> cachedDeserializationMethodNames = this.DeserializationMethodNames; Debug.Assert(!string.IsNullOrWhiteSpace(this.DeserializerTypeMetadataName)); Debug.Assert(!string.IsNullOrWhiteSpace(this.SerializationBinderPropertyMetadataName)); Debug.Assert(!cachedDeserializationMethodNames.IsEmpty); Debug.Assert(this.BinderDefinitelyNotSetDescriptor != null); Debug.Assert(this.BinderMaybeNotSetDescriptor != null); context.EnableConcurrentExecution(); // Security analyzer - analyze and report diagnostics on generated code. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); // For PropertySetAnalysis dataflow analysis. PropertyMapperCollection propertyMappers = new PropertyMapperCollection( new PropertyMapper( this.SerializationBinderPropertyMetadataName, PropertySetCallbacks.FlagIfNull)); HazardousUsageEvaluatorCollection hazardousUsageEvaluators = new HazardousUsageEvaluatorCollection( cachedDeserializationMethodNames.Select( methodName => new HazardousUsageEvaluator( methodName, PropertySetCallbacks.HazardousIfAllFlaggedOrAllUnknown))); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartAnalysisContext) => { if (!compilationStartAnalysisContext.Compilation.TryGetOrCreateTypeByMetadataName( this.DeserializerTypeMetadataName, out INamedTypeSymbol? deserializerTypeSymbol)) { return; } PooledHashSet <(IOperation Operation, ISymbol ContainingSymbol)> 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(BinderDefinitelyNotSetDescriptor !, owningSymbol, operationBlockStartAnalysisContext.Compilation, operationBlockStartAnalysisContext.CancellationToken) && operationBlockStartAnalysisContext.Options.IsConfiguredToSkipAnalysis(BinderMaybeNotSetDescriptor !, owningSymbol, operationBlockStartAnalysisContext.Compilation, operationBlockStartAnalysisContext.CancellationToken)) { return; } operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IObjectCreationOperation creationOperation = (IObjectCreationOperation)operationAnalysisContext.Operation; if (deserializerTypeSymbol.Equals(creationOperation.Type)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol)); } } }, OperationKind.ObjectCreation); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (Equals(invocationOperation.Instance?.Type, deserializerTypeSymbol) && cachedDeserializationMethodNames.Contains(invocationOperation.TargetMethod.Name)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol)); } } }, OperationKind.Invocation); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IMethodReferenceOperation methodReferenceOperation = (IMethodReferenceOperation)operationAnalysisContext.Operation; if (Equals(methodReferenceOperation.Instance?.Type, deserializerTypeSymbol) && cachedDeserializationMethodNames.Contains( methodReferenceOperation.Method.MetadataName)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add((operationAnalysisContext.Operation.GetRoot(), operationAnalysisContext.ContainingSymbol)); } } }, OperationKind.MethodReference); }); 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, this.DeserializerTypeMetadataName, DoNotUseInsecureDeserializerWithoutBinderBase.ConstructorMapper, propertyMappers, hazardousUsageEvaluators, InterproceduralAnalysisConfiguration.Create( compilationAnalysisContext.Options, SupportedDiagnostics, rootOperationsNeedingAnalysis.First().Operation.Syntax.SyntaxTree, compilationAnalysisContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: compilationAnalysisContext.CancellationToken)); } if (allResults == null) { return; } foreach (KeyValuePair <(Location Location, IMethodSymbol?Method), HazardousUsageEvaluationResult> kvp in allResults) { DiagnosticDescriptor descriptor; switch (kvp.Value) { case HazardousUsageEvaluationResult.Flagged: descriptor = this.BinderDefinitelyNotSetDescriptor !; break; case HazardousUsageEvaluationResult.MaybeFlagged: descriptor = this.BinderMaybeNotSetDescriptor !; 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); } }); }); }
protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation) { if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation, out var actualOperation, out var constraintExpression)) { return; } // Check if actual operation is a member access operation for either .Length or .Count if (actualOperation is IMemberReferenceOperation referenceOperation && IsInteger(referenceOperation.Type) && referenceOperation.Instance is IOperation instance && (referenceOperation.Member.Name is NUnitFrameworkConstants.NameOfHasLength || referenceOperation.Member.Name is NUnitFrameworkConstants.NameOfHasCount)) { // constraint operation must be Is. foreach (var constraintPart in constraintExpression.ConstraintParts) { if (!constraintPart.HasIncompatiblePrefixes() && constraintPart.Root is not null && constraintPart.HelperClass?.Name == NUnitFrameworkConstants.NameOfIs) { // Only raise to use Has.Property if the type at hand is IEnumerable. INamedTypeSymbol enumerable = context.Compilation.GetTypeByMetadataName("System.Collections.IEnumerable") !; if (instance.Type?.AllInterfaces.Any(x => SymbolEqualityComparer.Default.Equals(x, enumerable)) == true) { context.ReportDiagnostic(Diagnostic.Create( descriptor, referenceOperation.Syntax.GetLocation(), referenceOperation.Member.Name)); } } } }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilationContext.Compilation); TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind); if (sourceInfoSymbolMap.IsEmpty) { return; } TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; HashSet <IOperation> rootOperationsNeedingAnalysis = new HashSet <IOperation>(); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property)) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } }, OperationKind.PropertyReference); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceMethod(invocationOperation.TargetMethod)) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } }, OperationKind.Invocation); operationBlockStartContext.RegisterOperationBlockEndAction( operationBlockAnalysisContext => { if (!rootOperationsNeedingAnalysis.Any()) { return; } foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { TaintedDataAnalysisResult taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult( rootOperation.GetEnclosingControlFlowGraph(), operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, operationBlockAnalysisContext.Options, TaintedDataEnteringSinkDescriptor, sourceInfoSymbolMap, taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind), sinkInfoSymbolMap, operationBlockAnalysisContext.CancellationToken); if (taintedDataAnalysisResult == null) { return; } foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks) { if (!sourceSink.SinkKinds.Contains(this.SinkKind)) { continue; } foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins) { // Something like: // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'. Diagnostic diagnostic = Diagnostic.Create( this.TaintedDataEnteringSinkDescriptor, sourceSink.Sink.Location, additionalLocations: new Location[] { sourceOrigin.Location }, messageArgs: new object[] { sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationBlockAnalysisContext.ReportDiagnostic(diagnostic); } } } }); }); }); }
// FxCop compat: Catches things like static calls to File.Open() and Create() private static bool IsDisposableCreationSpecialCase(IInvocationOperation operation) => operation.TargetMethod.IsStatic && (operation.TargetMethod.Name.StartsWith("create", StringComparison.OrdinalIgnoreCase) || operation.TargetMethod.Name.StartsWith("open", StringComparison.OrdinalIgnoreCase));
protected override void AnalyzeAssertInvocation(OperationAnalysisContext context, IInvocationOperation assertOperation) { IOperation?actualOperation; IOperation?expectedOperation; if (assertOperation.TargetMethod.Name.Equals(NUnitFrameworkConstants.NameOfAssertAreEqual, StringComparison.Ordinal) || assertOperation.TargetMethod.Name.Equals(NUnitFrameworkConstants.NameOfAssertAreNotEqual, StringComparison.Ordinal)) { actualOperation = assertOperation.GetArgumentOperation(NUnitFrameworkConstants.NameOfActualParameter); expectedOperation = assertOperation.GetArgumentOperation(NUnitFrameworkConstants.NameOfExpectedParameter); CheckActualVsExpectedOperation(context, actualOperation, expectedOperation); } else { if (!AssertHelper.TryGetActualAndConstraintOperations(assertOperation, out actualOperation, out var constraintExpression)) { return; } foreach (var constraintPartExpression in constraintExpression.ConstraintParts) { if (constraintPartExpression.HasIncompatiblePrefixes() || HasCustomEqualityComparer(constraintPartExpression) || constraintPartExpression.HasUnknownExpressions()) { return; } var constraintMethod = constraintPartExpression.GetConstraintMethod(); if (constraintMethod?.Name != NUnitFrameworkConstants.NameOfIsEqualTo || constraintMethod.ReturnType?.GetFullMetadataName() != NUnitFrameworkConstants.FullNameOfEqualToConstraint) { continue; } expectedOperation = constraintPartExpression.GetExpectedArgument(); CheckActualVsExpectedOperation(context, actualOperation, expectedOperation); } } }
private async Task <Solution> ExtractDependency(Document document, InvocationExpressionSyntax invocationSyntax, IdentifierNameSyntax invokedMethodIdentifierSyntax, SemanticModel semanticModel, CancellationToken cancellationToken, IInvocationOperation invocationOperation, Maybe <ImmutableArray <WhatToDoWithArgument> > whatToDoWithArgsMaybe, BaseMethodDeclarationSyntax containingMethod) { var solution = document.Project.Solution; if (whatToDoWithArgsMaybe.HasNoValue) { return(solution); } var whatToDoWithArgs = whatToDoWithArgsMaybe.GetValue(); var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var nodesToReplace = new Dictionary <Document, List <NodeChange> >(); void AddNewChangeToDocument(Document doc, NodeChange change) { nodesToReplace.GetOrAdd(doc, () => new List <NodeChange>()).Add(change); } var argsAndWhatToDoWithThem = invocationOperation.Arguments .Zip(whatToDoWithArgs, (arg, whatTodo) => (arg, whatTodo)) .ToImmutableArray(); var(parameterListChange, replacementFunctionParameterName, parametersToRemove) = GetChangeToContainingMethodParameters( document, invokedMethodIdentifierSyntax, semanticModel, argsAndWhatToDoWithThem, containingMethod, documentRoot, invocationOperation, whatToDoWithArgs, invocationSyntax); AddNewChangeToDocument( document, parameterListChange); var nodeChange = GetMethodInvocationChange( invocationSyntax, invokedMethodIdentifierSyntax, argsAndWhatToDoWithThem, replacementFunctionParameterName); AddNewChangeToDocument(document, nodeChange); var changesToCallers = await GetChangesToCallers( semanticModel, cancellationToken, containingMethod, solution, invocationOperation.TargetMethod, argsAndWhatToDoWithThem, parametersToRemove, invocationSyntax).ConfigureAwait(false); foreach (var changeToCallers in changesToCallers) { AddNewChangeToDocument(changeToCallers.document, changeToCallers.change); } return(await UpdateSolution(cancellationToken, solution, nodesToReplace).ConfigureAwait(false)); }