File: src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis\Analysis\TaintedDataAnalysis\TaintedDataSymbolMapExtensions.cs
Web Access
Project: src\src\RoslynAnalyzers\Microsoft.CodeAnalysis.AnalyzerUtilities\Microsoft.CodeAnalysis.AnalyzerUtilities.csproj (Microsoft.CodeAnalysis.AnalyzerUtilities)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis
{
    using ValueContentAnalysisResult = DataFlowAnalysisResult<ValueContentBlockAnalysisResult, ValueContentAbstractValue>;
 
    internal static class TaintedDataSymbolMapExtensions
    {
        /// <summary>
        /// Determines if the given method is a tainted data source and get the tainted target set.
        /// </summary>
        /// <param name="sourceSymbolMap"></param>
        /// <param name="method"></param>
        /// <param name="arguments"></param>
        /// <param name="pointsToFactory">If the method needs to do PointsToAnalysis, the PointsToAnalysis result will be produced by the passed value factory.</param>
        /// <param name="valueContentFactory">If the method needs to do ValueContentAnalysis, the ValueContentAnalysis result will be produced by the passed value factory.</param>
        /// <param name="allTaintedTargets"></param>
        /// <returns></returns>
        public static bool IsSourceMethod(
            this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap,
            IMethodSymbol method,
            ImmutableArray<IArgumentOperation> arguments,
            Lazy<PointsToAnalysisResult?> pointsToFactory,
            Lazy<(PointsToAnalysisResult? p, ValueContentAnalysisResult? v)> valueContentFactory,
            [NotNullWhen(returnValue: true)] out PooledHashSet<string>? allTaintedTargets)
        {
            allTaintedTargets = null;
            PointsToAnalysisResult? pointsToAnalysisResult = null;
            ValueContentAnalysisResult? valueContentAnalysisResult = null;
            foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(method.ContainingType))
            {
                foreach ((MethodMatcher methodMatcher, ImmutableHashSet<string> taintedTargets) in sourceInfo.TaintedMethods)
                {
                    if (methodMatcher(method.Name, arguments))
                    {
                        allTaintedTargets ??= PooledHashSet<string>.GetInstance();
 
                        allTaintedTargets.UnionWith(taintedTargets);
                    }
                }
 
                foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(PointsToCheck pointsToCheck, string)> pointsToTaintedTargets) in sourceInfo.TaintedMethodsNeedsPointsToAnalysis)
                {
                    if (pointsToTaintedTargets.Any() && methodMatcher(method.Name, arguments))
                    {
                        pointsToAnalysisResult ??= pointsToFactory.Value;
                        if (pointsToAnalysisResult == null)
                        {
                            break;
                        }
 
                        IEnumerable<(PointsToCheck, string target)> positivePointsToTaintedTargets = pointsToTaintedTargets.Where(s =>
                            s.pointsToCheck(
                                arguments.SelectAsArray(o =>
                                    pointsToAnalysisResult[o.Kind, o.Syntax])));
                        if (positivePointsToTaintedTargets.Any())
                        {
                            allTaintedTargets ??= PooledHashSet<string>.GetInstance();
 
                            allTaintedTargets.UnionWith(positivePointsToTaintedTargets.Select(s => s.target));
                        }
                    }
                }
 
                foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(ValueContentCheck valueContentCheck, string)> valueContentTaintedTargets) in sourceInfo.TaintedMethodsNeedsValueContentAnalysis)
                {
                    if (valueContentTaintedTargets.Any() && methodMatcher(method.Name, arguments))
                    {
                        pointsToAnalysisResult ??= valueContentFactory.Value.p;
                        valueContentAnalysisResult ??= valueContentFactory.Value.v;
                        if (pointsToAnalysisResult == null || valueContentAnalysisResult == null)
                        {
                            break;
                        }
 
                        IEnumerable<(ValueContentCheck, string target)> positiveValueContentTaintedTargets = valueContentTaintedTargets.Where(s =>
                            s.valueContentCheck(
                                arguments.SelectAsArray(o => pointsToAnalysisResult[o.Kind, o.Syntax]),
                                arguments.SelectAsArray(o => valueContentAnalysisResult[o.Kind, o.Syntax])));
                        if (positiveValueContentTaintedTargets.Any())
                        {
                            allTaintedTargets ??= PooledHashSet<string>.GetInstance();
 
                            allTaintedTargets.UnionWith(positiveValueContentTaintedTargets.Select(s => s.target));
                        }
                    }
                }
            }
 
            return allTaintedTargets != null;
        }
 
        /// <summary>
        /// Determines if the given property is a tainted data source.
        /// </summary>
        /// <param name="sourceSymbolMap"></param>
        /// <param name="propertySymbol"></param>
        /// <returns></returns>
        public static bool IsSourceProperty(this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap, IPropertySymbol propertySymbol)
        {
            foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(propertySymbol.ContainingType))
            {
                if (sourceInfo.TaintedProperties.Contains(propertySymbol.MetadataName))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Determines if the given parameter is a tainted data source.
        /// </summary>
        /// <param name="sourceSymbolMap"></param>
        /// <param name="parameterSymbol"></param>
        /// <returns></returns>
        public static bool IsSourceParameter(this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap, IParameterSymbol parameterSymbol, WellKnownTypeProvider wellKnownTypeProvider)
        {
            ISymbol containingSymbol = parameterSymbol.ContainingSymbol;
            foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(containingSymbol.ContainingType))
            {
                if (sourceInfo.TaintedArguments.Any(match => match(parameterSymbol, wellKnownTypeProvider)))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Determines if the given array can be a tainted data source when its elements are all constant.
        /// </summary>
        /// <param name="sourceSymbolMap"></param>
        /// <param name="arrayTypeSymbol"></param>
        /// <returns></returns>
        public static bool IsSourceConstantArrayOfType(
            this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap,
            IArrayTypeSymbol arrayTypeSymbol,
            IArrayInitializerOperation arrayInitializerOperation)
        {
            if (arrayTypeSymbol.ElementType is INamedTypeSymbol elementType)
            {
                foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(elementType))
                {
                    if (sourceInfo.TaintConstantArray
                        && (sourceInfo.ConstantArrayLengthMatcher == null
                            || sourceInfo.ConstantArrayLengthMatcher(arrayInitializerOperation.ElementValues.Length)))
                    {
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Determines if the method taints other arguments cause some arguments are tainted.
        /// </summary>
        /// <param name="sourceSymbolMap"></param>
        /// <param name="method"></param>
        /// <param name="taintedParameterNames"></param>
        /// <param name="taintedParameterPairs">The set of parameter pairs (tainted source parameter name, tainted end parameter name).</param>
        /// <returns></returns>
        public static bool IsSourceTransferMethod(
            this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap,
            IMethodSymbol method,
            ImmutableArray<IArgumentOperation> arguments,
            ISet<string> taintedParameterNames,
            [NotNullWhen(returnValue: true)] out PooledHashSet<(string, string)>? taintedParameterPairs)
        {
            taintedParameterPairs = null;
            foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(method.ContainingType))
            {
                foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(string source, string end)> sourceToEnds) in sourceInfo.TransferMethods)
                {
                    if (methodMatcher(method.Name, arguments))
                    {
                        taintedParameterPairs ??= PooledHashSet<(string, string)>.GetInstance();
 
                        taintedParameterPairs.UnionWith(sourceToEnds.Where(s => taintedParameterNames.Contains(s.source)));
                    }
                }
            }
 
            return taintedParameterPairs != null;
        }
 
        /// <summary>
        /// Determines if the property taints the instance.
        /// </summary>
        public static bool IsSourceTransferProperty(
            this TaintedDataSymbolMap<SourceInfo> sourceSymbolMap,
            IPropertyReferenceOperation propertyReferenceOperation)
        {
            if (propertyReferenceOperation.Instance?.Type is not INamedTypeSymbol namedType)
            {
                return false;
            }
 
            string name = propertyReferenceOperation.Member.Name;
            if (propertyReferenceOperation.Member.Language != LanguageNames.CSharp && propertyReferenceOperation.Member.IsIndexer())
            {
                name = TaintedDataProperties.IndexerName; // In VB.NET for example the indexer name is `Item`. However let's keep the SourceInfo configuration language agnostic.
            }
 
            foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(namedType))
            {
                if (sourceInfo.TransferProperties.Contains(name))
                {
                    return true;
                }
            }
 
            return false;
        }
    }
}