private static bool ValueIsAccessed(SemanticModel semanticModel, IConditionalOperation ifOperation, IBlockOperation containingBlock, ISymbol localOrParameter, IExpressionStatementOperation expressionStatement, IAssignmentOperation assignmentExpression) { var statements = containingBlock.Operations; var ifOperationIndex = statements.IndexOf(ifOperation); var expressionStatementIndex = statements.IndexOf(expressionStatement); if (expressionStatementIndex > ifOperationIndex + 1) { // There are intermediary statements between the check and the assignment. // Make sure they don't try to access the local. var dataFlow = semanticModel.AnalyzeDataFlow( statements[ifOperationIndex + 1].Syntax, statements[expressionStatementIndex - 1].Syntax); if (dataFlow.ReadInside.Contains(localOrParameter) || dataFlow.WrittenInside.Contains(localOrParameter)) { return(true); } } // Also, have to make sure there is no read/write of the local/parameter on the left // of the assignment. For example: map[val.Id] = val; var exprDataFlow = semanticModel.AnalyzeDataFlow(assignmentExpression.Target.Syntax); return(exprDataFlow.ReadInside.Contains(localOrParameter) || exprDataFlow.WrittenInside.Contains(localOrParameter)); }
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartAnalysisContext) => { if (!compilationStartAnalysisContext.Compilation.TryGetOrCreateTypeByMetadataName( WellKnownTypeNames.NewtonsoftJsonTypeNameHandling, out INamedTypeSymbol? typeNameHandlingSymbol)) { return; } compilationStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IFieldReferenceOperation fieldReferenceOperation = (IFieldReferenceOperation)operationAnalysisContext.Operation; if (IsOtherThanNone(fieldReferenceOperation)) { operationAnalysisContext.ReportDiagnostic( fieldReferenceOperation.CreateDiagnostic(Rule)); } }, OperationKind.FieldReference); compilationStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IAssignmentOperation assignmentOperation = (IAssignmentOperation)operationAnalysisContext.Operation; if (!typeNameHandlingSymbol.Equals(assignmentOperation.Target.Type)) { return; } // Find the topmost operation with non-zero (not None), unless we find an operation that would've // been flagged by the FieldReference callback above. foreach (IOperation childOperation in assignmentOperation.Value.DescendantsAndSelf()) { if (childOperation is IFieldReferenceOperation fieldReferenceOperation && IsOtherThanNone(fieldReferenceOperation)) { return; } if (childOperation.ConstantValue.HasValue && childOperation.ConstantValue.Value is int integerValue && integerValue != 0) { operationAnalysisContext.ReportDiagnostic(childOperation.CreateDiagnostic(Rule)); return; } } }, OperationKind.SimpleAssignment, OperationKind.CompoundAssignment); return; bool IsOtherThanNone(IFieldReferenceOperation fieldReferenceOperation) { RoslynDebug.Assert(typeNameHandlingSymbol != null); if (!typeNameHandlingSymbol.Equals(fieldReferenceOperation.Field.ContainingType)) { return(false); } return(fieldReferenceOperation.Field.Name != "None"); }; }); }
private void VisitAssignment(IAssignmentOperation operation) { AssertEx.Equal(new[] { operation.Target, operation.Value }, operation.Children); }
public override void Initialize(AnalysisContext context) { if (!Debugger.IsAttached) // prefer single thread for debugging in development { context.EnableConcurrentExecution(); } if (context.IsAuditMode()) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); } else { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); } context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { var config = Configuration.GetOrCreate(compilationContext); if (config.AuditMode) { TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = config.TaintConfiguration.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } Compilation compilation = compilationContext.Compilation; compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; AnalyzerOptions options = operationBlockStartContext.Options; CancellationToken cancellationToken = operationBlockStartContext.CancellationToken; if (options.IsConfiguredToSkipAnalysis(TaintedDataEnteringSinkDescriptor, owningSymbol, compilation, cancellationToken)) { return; } WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); void CreateWarning(OperationAnalysisContext operationAnalysisContext, Location location, ISymbol paramSymbol, ISymbol symbol) { // 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, location, additionalLocations: new Location[] { location }, messageArgs: new object[] { paramSymbol.Name, symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationAnalysisContext.ReportDiagnostic(diagnostic); } bool IsConstant(IOperation operation, IOperation value, OperationAnalysisContext operationAnalysisContext) { if (value.ConstantValue.HasValue || value is ITypeOfOperation) { return(true); } if (!operation.TryGetEnclosingControlFlowGraph(out var cfg)) { return(false); } var valueContentResult = ValueContentAnalysis.TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider, operationAnalysisContext.Options, TaintedDataEnteringSinkDescriptor, PointsToAnalysisKind.Complete, operationAnalysisContext.CancellationToken); if (valueContentResult == null) { return(false); } ValueContentAbstractValue abstractValue = valueContentResult[value.Kind, value.Syntax]; return(abstractValue.NonLiteralState == ValueContainsNonLiteralState.No); } operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IAssignmentOperation operation = (IAssignmentOperation)operationAnalysisContext.Operation; if (!(operation.Target is IPropertyReferenceOperation propertyReferenceOperation)) { return; } IEnumerable <SinkInfo>?infosForType = sinkInfoSymbolMap.GetInfosForType(propertyReferenceOperation.Member.ContainingType); if (infosForType != null && infosForType.Any(x => x.SinkProperties.Contains(propertyReferenceOperation.Member.MetadataName)) && !IsConstant(operation, operation.Value, operationAnalysisContext)) { CreateWarning( operationAnalysisContext, propertyReferenceOperation.Syntax.GetLocation(), operation.Value.Type, propertyReferenceOperation.Member); } }, OperationKind.SimpleAssignment); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; IEnumerable <SinkInfo>?infosForType = sinkInfoSymbolMap.GetInfosForType(invocationOperation.TargetMethod.ContainingType); if (infosForType == null) { return; } foreach (SinkInfo sinkInfo in infosForType) { foreach (IArgumentOperation taintedArgument in invocationOperation.Arguments.Where(x => !IsConstant(x, x.Value, operationAnalysisContext))) { if (sinkInfo.SinkMethodParameters.TryGetValue(invocationOperation.TargetMethod.MetadataName, out ImmutableHashSet <string> sinkParameters) && sinkParameters.Contains(taintedArgument.Parameter.MetadataName)) { CreateWarning(operationAnalysisContext, invocationOperation.Syntax.GetLocation(), taintedArgument.Parameter, invocationOperation.TargetMethod); return; } } } }, OperationKind.Invocation); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IObjectCreationOperation invocationOperation = (IObjectCreationOperation)operationAnalysisContext.Operation; IEnumerable <SinkInfo>?infosForType = sinkInfoSymbolMap.GetInfosForType(invocationOperation.Constructor.ContainingType); if (infosForType == null) { return; } foreach (SinkInfo sinkInfo in infosForType) { foreach (IArgumentOperation taintedArgument in invocationOperation.Arguments.Where(x => !IsConstant(x, x.Value, operationAnalysisContext))) { if (sinkInfo.IsAnyStringParameterInConstructorASink && taintedArgument.Parameter.Type.SpecialType == SpecialType.System_String) { CreateWarning(operationAnalysisContext, invocationOperation.Syntax.GetLocation(), taintedArgument.Parameter, invocationOperation.Constructor); return; } else if (sinkInfo.SinkMethodParameters.TryGetValue(invocationOperation.Constructor.MetadataName, out ImmutableHashSet <string> sinkParameters) && sinkParameters.Contains(taintedArgument.Parameter.MetadataName)) { CreateWarning(operationAnalysisContext, invocationOperation.Syntax.GetLocation(), taintedArgument.Parameter, invocationOperation.Constructor); return; } } } }, OperationKind.ObjectCreation); }); } else { TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = config.TaintConfiguration.GetSourceSymbolMap(this.SinkKind); if (sourceInfoSymbolMap.IsEmpty) { return; } TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = config.TaintConfiguration.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } Compilation compilation = compilationContext.Compilation; compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; AnalyzerOptions options = operationBlockStartContext.Options; CancellationToken cancellationToken = operationBlockStartContext.CancellationToken; if (options.IsConfiguredToSkipAnalysis(TaintedDataEnteringSinkDescriptor, owningSymbol, compilation, cancellationToken)) { return; } WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); Lazy <ControlFlowGraph?> controlFlowGraphFactory = new Lazy <ControlFlowGraph?>( () => operationBlockStartContext.OperationBlocks.GetControlFlowGraph()); Lazy <PointsToAnalysisResult?> pointsToFactory = new Lazy <PointsToAnalysisResult?>( () => { if (controlFlowGraphFactory.Value == null) { return(null); } InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create( options, SupportedDiagnostics, controlFlowGraphFactory.Value, operationBlockStartContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: cancellationToken, defaultMaxInterproceduralMethodCallChain: config.MaxInterproceduralMethodCallChain, defaultMaxInterproceduralLambdaOrLocalFunctionCallChain: config.MaxInterproceduralLambdaOrLocalFunctionCallChain); return(PointsToAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, PointsToAnalysisKind.Complete, interproceduralAnalysisConfiguration, interproceduralAnalysisPredicate: null)); }); Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)> valueContentFactory = new Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)>( () => { if (controlFlowGraphFactory.Value == null) { return(null, null); } InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create( options, SupportedDiagnostics, controlFlowGraphFactory.Value, operationBlockStartContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: cancellationToken, defaultMaxInterproceduralMethodCallChain: config.MaxInterproceduralMethodCallChain, defaultMaxInterproceduralLambdaOrLocalFunctionCallChain: config.MaxInterproceduralLambdaOrLocalFunctionCallChain); ValueContentAnalysisResult?valuecontentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, PointsToAnalysisKind.Complete, interproceduralAnalysisConfiguration, out _, out PointsToAnalysisResult? p); return(p, valuecontentAnalysisResult); }); var 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); if (sourceInfoSymbolMap.RequiresFieldReferenceAnalysis) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IFieldReferenceOperation fieldReferenceOperation = (IFieldReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceField(fieldReferenceOperation.Field)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(fieldReferenceOperation.GetRoot()); } } }, OperationKind.FieldReference); } if (sourceInfoSymbolMap.RequiresParameterReferenceAnalysis) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IParameterReferenceOperation parameterReferenceOperation = (IParameterReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceParameter(parameterReferenceOperation.Parameter, wellKnownTypeProvider)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(parameterReferenceOperation.GetRoot()); } } }, OperationKind.ParameterReference); } 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 (config.TaintConfiguration.HasTaintArraySource(SinkKind, config)) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IArrayInitializerOperation arrayInitializerOperation = (IArrayInitializerOperation)operationAnalysisContext.Operation; if (arrayInitializerOperation.GetAncestor <IArrayCreationOperation>(OperationKind.ArrayCreation)?.Type is IArrayTypeSymbol arrayTypeSymbol && sourceInfoSymbolMap.IsSourceConstantArrayOfType(arrayTypeSymbol, arrayInitializerOperation)) { 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, config.TaintConfiguration.GetSanitizerSymbolMap(this.SinkKind), sinkInfoSymbolMap, operationBlockAnalysisContext.CancellationToken, config.MaxInterproceduralMethodCallChain, config.MaxInterproceduralLambdaOrLocalFunctionCallChain); 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.Name, sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationBlockAnalysisContext.ReportDiagnostic(diagnostic); } } } } } finally { rootOperationsNeedingAnalysis.Free(compilationContext.CancellationToken); } }); }); } }); }
private Task VisitAssignmentOperationAsync(IAssignmentOperation assignmentOperation, CancellationToken cancellationToken) => VisitAsync(assignmentOperation.Value, cancellationToken);
public override void Initialize(AnalysisContext context) { if (!Debugger.IsAttached) // prefer single thread for debugging in development { context.EnableConcurrentExecution(); } if (context.IsAuditMode()) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); } else { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); } if (!Debugger.IsAttached) // prefer single thread for debugging in development { context.EnableConcurrentExecution(); } if (context.IsAuditMode()) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); } else { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); } context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { Compilation compilation = compilationContext.Compilation; var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); if (!wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName("System.Xml.Xsl.XsltSettings", out var type)) { return; } var configuration = Configuration.GetOrCreate(compilationContext); compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; AnalyzerOptions options = operationBlockStartContext.Options; CancellationToken cancellationToken = operationBlockStartContext.CancellationToken; if (options.IsConfiguredToSkipAnalysis(Rule, owningSymbol, compilation, cancellationToken)) { return; } bool?GetValue(IOperation operation, IOperation value, OperationAnalysisContext operationAnalysisContext) { if (value.ConstantValue.HasValue && value.ConstantValue.Value is bool isEnableScript) { return(isEnableScript); } if (!operation.TryGetEnclosingControlFlowGraph(out var cfg)) { return(null); } var valueContentResult = ValueContentAnalysis.TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider, operationAnalysisContext.Options, Rule, PointsToAnalysisKind.Complete, operationAnalysisContext.CancellationToken); if (valueContentResult == null) { return(null); } ValueContentAbstractValue abstractValue = valueContentResult[value.Kind, value.Syntax]; PropertySetAbstractValueKind kind = PropertySetCallbacks.EvaluateLiteralValues(abstractValue, (object?o) => o is true); if (kind == PropertySetAbstractValueKind.MaybeFlagged || kind == PropertySetAbstractValueKind.Flagged) { return(true); } kind = PropertySetCallbacks.EvaluateLiteralValues(abstractValue, (object?o) => o is false); if (kind == PropertySetAbstractValueKind.Flagged) { return(false); } return(null); } operationBlockStartContext.RegisterOperationAction( ctx => { IAssignmentOperation operation = (IAssignmentOperation)ctx.Operation; if (!(operation.Target is IPropertyReferenceOperation propertyReferenceOperation)) { return; } if (propertyReferenceOperation.Member.ContainingType != type) { return; } if (propertyReferenceOperation.Member.Name == "EnableScript") { var enableScript = GetValue(operation, operation.Value, ctx); if ((enableScript.HasValue && enableScript.Value) || !enableScript.HasValue && configuration.AuditMode) { ctx.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation())); } } }, OperationKind.SimpleAssignment); operationBlockStartContext.RegisterOperationAction( ctx => { var operation = (IPropertyReferenceOperation)ctx.Operation; if (operation.Property.ContainingType != type || operation.Property.Name != "TrustedXslt") { return; } ctx.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation())); }, OperationKind.PropertyReference); operationBlockStartContext.RegisterOperationAction( ctx => { IObjectCreationOperation invocationOperation = (IObjectCreationOperation)ctx.Operation; if (invocationOperation.Constructor.ContainingType != type) { return; } var enableScriptArg = invocationOperation.Arguments.FirstOrDefault(x => x.Parameter.Name == "enableScript"); if (enableScriptArg == null) { return; } var enableScript = GetValue(invocationOperation, enableScriptArg.Value, ctx); if ((enableScript.HasValue && enableScript.Value) || !enableScript.HasValue && configuration.AuditMode) { ctx.ReportDiagnostic(Diagnostic.Create(Rule, invocationOperation.Syntax.GetLocation())); } }, OperationKind.ObjectCreation); }); }); }