private static bool ReportDiagnosticIfNecessary(OperationAnalysisContext operationContext, IOperation argumentValue, SyntaxNode syntax, ISymbol invokedSymbol, ISymbol containingMethod) { if (argumentValue.Type.SpecialType != SpecialType.System_String || !argumentValue.ConstantValue.HasValue) { // We have a candidate for diagnostic. perform more precise dataflow analysis. var topmostBlock = argumentValue.GetTopmostParentBlock(); if (topmostBlock != null) { var cfg = ControlFlowGraph.Create(topmostBlock); var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation); var copyAnalysisResult = CopyAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider); var nullAnalysisResult = NullAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider); var pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, nullAnalysisResult); var stringContentResult = StringContentAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, copyAnalysisResult, nullAnalysisResult, pointsToAnalysisResult); StringContentAbstractValue value = stringContentResult[argumentValue]; if (value.NonLiteralState == StringContainsNonLiteralState.No) { // The value is a constant literal or default/unitialized, so avoid flagging this usage. return(false); } } // Review if the symbol passed to {invocation} in {method/field/constructor/etc} has user input. operationContext.ReportDiagnostic(syntax.CreateDiagnostic(Rule, invokedSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), containingMethod.Name)); return(true); } return(false); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol localizableStateAttributeSymbol = WellKnownTypes.LocalizableAttribute(compilationContext.Compilation); INamedTypeSymbol conditionalAttributeSymbol = WellKnownTypes.ConditionalAttribute(compilationContext.Compilation); INamedTypeSymbol systemConsoleSymbol = WellKnownTypes.Console(compilationContext.Compilation); ImmutableHashSet <INamedTypeSymbol> typesToIgnore = GetTypesToIgnore(compilationContext.Compilation); compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext => { if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod)) { return; } var lazyStringContentResult = new Lazy <DataFlowAnalysisResult <StringContentBlockAnalysisResult, StringContentAbstractValue> >( valueFactory: ComputeStringContentAnalysisResult, isThreadSafe: false); operationBlockStartContext.RegisterOperationAction(operationContext => { var argument = (IArgumentOperation)operationContext.Operation; switch (argument.Parent?.Kind) { case OperationKind.Invocation: case OperationKind.ObjectCreation: AnalyzeArgument(argument.Parameter, containingPropertySymbolOpt: null, operation: argument, reportDiagnostic: operationContext.ReportDiagnostic); return; } }, OperationKind.Argument); operationBlockStartContext.RegisterOperationAction(operationContext => { var propertyReference = (IPropertyReferenceOperation)operationContext.Operation; if (propertyReference.Parent is IAssignmentOperation assignment && assignment.Target == propertyReference && !propertyReference.Property.IsIndexer && propertyReference.Property.SetMethod?.Parameters.Length == 1) { IParameterSymbol valueSetterParam = propertyReference.Property.SetMethod.Parameters[0]; AnalyzeArgument(valueSetterParam, propertyReference.Property, assignment, operationContext.ReportDiagnostic); } }, OperationKind.PropertyReference); void AnalyzeArgument(IParameterSymbol parameter, IPropertySymbol containingPropertySymbolOpt, IOperation operation, Action <Diagnostic> reportDiagnostic) { if (ShouldBeLocalized(parameter, containingPropertySymbolOpt, localizableStateAttributeSymbol, conditionalAttributeSymbol, systemConsoleSymbol, typesToIgnore)) { StringContentAbstractValue stringContentValue = lazyStringContentResult.Value[operation]; if (stringContentValue.IsLiteralState) { Debug.Assert(stringContentValue.LiteralValues.Count > 0); // FxCop compat: Do not fire if the literal value came from a default parameter value if (stringContentValue.LiteralValues.Count == 1 && parameter.IsOptional && parameter.ExplicitDefaultValue is string defaultValue && defaultValue == stringContentValue.LiteralValues.Single()) { return; } // FxCop compat: Do not fire if none of the string literals have any non-control character. if (!LiteralValuesHaveNonControlCharacters(stringContentValue.LiteralValues)) { return; } // FxCop compat: Filter out xml string literals. var filteredStrings = stringContentValue.LiteralValues.Where(literal => !LooksLikeXmlTag(literal)); if (filteredStrings.Any()) { // Method '{0}' passes a literal string as parameter '{1}' of a call to '{2}'. Retrieve the following string(s) from a resource table instead: "{3}". var arg1 = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg2 = parameter.Name; var arg3 = parameter.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var arg4 = FormatLiteralValues(filteredStrings); var diagnostic = operation.CreateDiagnostic(Rule, arg1, arg2, arg3, arg4); reportDiagnostic(diagnostic); } } } } DataFlowAnalysisResult <StringContentBlockAnalysisResult, StringContentAbstractValue> ComputeStringContentAnalysisResult() { foreach (var operationRoot in operationBlockStartContext.OperationBlocks) { IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null) { var cfg = ControlFlowGraph.Create(topmostBlock); var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationBlockStartContext.Compilation); var pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider); var copyAnalysisResult = CopyAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, pointsToAnalysisResultOpt: pointsToAnalysisResult); // Do another analysis pass to improve the results from PointsTo and Copy analysis. pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, pointsToAnalysisResult, copyAnalysisResult); return(StringContentAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, copyAnalysisResult, pointsToAnalysisResult)); } } return(null); } }); }); }