| File: Microsoft.NetFramework.Analyzers\DoNotUseInsecureXSLTScriptExecution.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Concurrent; using System.Collections.Immutable; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.NetFramework.Analyzers.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetFramework.Analyzers { using static MicrosoftNetFrameworkAnalyzersResources; /// <summary> /// CA3076: <inheritdoc cref="InsecureXsltScriptProcessingMessage"/> /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DoNotUseInsecureXSLTScriptExecutionAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA3076"; internal static readonly DiagnosticDescriptor RuleDoNotUseInsecureXSLTScriptExecution = DiagnosticDescriptorHelper.Create( RuleId, CreateLocalizableResourceString(nameof(InsecureXsltScriptProcessingMessage)), CreateLocalizableResourceString(nameof(DoNotUseInsecureDtdProcessingGenericMessage)), DiagnosticCategory.Security, RuleLevel.IdeHidden_BulkConfigurable, CreateLocalizableResourceString(nameof(DoNotUseInsecureXSLTScriptExecutionDescription)), isPortedFxCopRule: false, isDataflowRule: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(RuleDoNotUseInsecureXSLTScriptExecution); #pragma warning disable RS1026 // Enable concurrent execution public override void Initialize(AnalysisContext context) #pragma warning restore RS1026 // Enable concurrent execution { //analysisContext.EnableConcurrentExecution(); // Security analyzer - analyze and report diagnostics in generated code. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction( context => { Compilation compilation = context.Compilation; var xmlTypes = new CompilationSecurityTypes(compilation); if (xmlTypes.XslCompiledTransform == null) { return; } context.RegisterOperationBlockStartAction(context => { var analyzer = new OperationBlockAnalyzer(xmlTypes, context.OwningSymbol); context.RegisterOperationAction(context => { var assignment = (IAssignmentOperation)context.Operation; analyzer.AnalyzeNodeForXsltSettings(assignment.Target, assignment.Value); }, OperationKind.SimpleAssignment); context.RegisterOperationAction(context => { var variableDeclarator = (IVariableDeclaratorOperation)context.Operation; if (variableDeclarator.GetVariableInitializer() is IVariableInitializerOperation initializer) { analyzer.AnalyzeNodeForXsltSettings(variableDeclarator, initializer.Value); } }, OperationKind.VariableDeclarator); context.RegisterOperationAction(context => { var invocation = (IInvocationOperation)context.Operation; analyzer.AnalyzeNodeForXslCompiledTransformLoad( invocation.TargetMethod, invocation.Arguments, static (context, diagnostic) => context.ReportDiagnostic(diagnostic), context); }, OperationKind.Invocation); context.RegisterOperationAction(context => { var objectCreation = (IObjectCreationOperation)context.Operation; analyzer.AnalyzeNodeForXslCompiledTransformLoad( objectCreation.Constructor, objectCreation.Arguments, static (context, diagnostic) => context.ReportDiagnostic(diagnostic), context); }, OperationKind.ObjectCreation); }); }); } private sealed class OperationBlockAnalyzer { private readonly CompilationSecurityTypes _xmlTypes; private readonly ISymbol _enclosingSymbol; private readonly ConcurrentDictionary<ISymbol, XsltSettingsEnvironment> _xsltSettingsEnvironments = new(); public OperationBlockAnalyzer(CompilationSecurityTypes xmlTypes, ISymbol enclosingSymbol) { _xmlTypes = xmlTypes; _enclosingSymbol = enclosingSymbol; } public void AnalyzeNodeForXslCompiledTransformLoad<TContext>(IMethodSymbol? methodSymbol, ImmutableArray<IArgumentOperation> arguments, Action<TContext, Diagnostic> reportDiagnostic, TContext context) { if (!methodSymbol.IsXslCompiledTransformLoad(_xmlTypes)) { return; } int xmlResolverIndex = methodSymbol.GetXmlResolverParameterIndex(_xmlTypes); int xsltSettingsIndex = methodSymbol.GetXsltSettingsParameterIndex(_xmlTypes); // Overloads with no XmlResolver and XstlSettings specified are secure since they all have // following behavior: // 1. An XmlUrlResolver with no user credentials is used to process any xsl:import // or xsl:include elements. // 2. The document() function is disabled. // 3. Embedded scripts are not supported. if (xmlResolverIndex < 0 || xsltSettingsIndex < 0) { return; } var resolverArgumentOperation = arguments[xmlResolverIndex].Value; if (resolverArgumentOperation is IConversionOperation conversion) { resolverArgumentOperation = conversion.Operand; } var isSecureResolver = resolverArgumentOperation.HasNullConstantValue() || resolverArgumentOperation.Type.IsXmlSecureResolverType(_xmlTypes); var settingsOperation = arguments[xsltSettingsIndex].Value; var settingsSymbol = settingsOperation.GetReferencedMemberOrLocalOrParameter(); bool isSecureSettings; bool isSetInBlock; // 1. pass null or XsltSettings.Default as XsltSetting : secure if (settingsSymbol is null || (settingsSymbol as IPropertySymbol).IsXsltSettingsDefaultProperty(_xmlTypes)) { isSetInBlock = true; isSecureSettings = true; } // 2. XsltSettings.TrustedXslt : insecure else if ((settingsSymbol as IPropertySymbol).IsXsltSettingsTrustedXsltProperty(_xmlTypes)) { isSetInBlock = true; isSecureSettings = false; } // 3. check xsltSettingsEnvironments, if IsScriptDisabled && IsDocumentFunctionDisabled then secure, // else insecure else if (_xsltSettingsEnvironments.TryGetValue(settingsSymbol, out XsltSettingsEnvironment env)) { isSetInBlock = false; isSecureSettings = env.IsDocumentFunctionDisabled && env.IsScriptDisabled; } //4. symbol for settings is not found => passed in without any change => assume insecure else { isSetInBlock = true; isSecureSettings = false; } if (!isSecureSettings && !isSecureResolver) { LocalizableResourceString message = CreateLocalizableResourceString( isSetInBlock ? nameof(XslCompiledTransformLoadInsecureConstructedMessage) : nameof(XslCompiledTransformLoadInsecureInputMessage), SecurityDiagnosticHelpers.GetNonEmptyParentName(_enclosingSymbol) ); var invocationOrObjCreation = settingsOperation.Parent!.Parent!; RoslynDebug.Assert(invocationOrObjCreation.Kind is OperationKind.Invocation or OperationKind.ObjectCreation); reportDiagnostic( context, invocationOrObjCreation.CreateDiagnostic(RuleDoNotUseInsecureXSLTScriptExecution, message)); } } public void AnalyzeNodeForXsltSettings(IOperation lhs, IOperation rhs) { var lhsSymbol = lhs.Kind switch { OperationKind.VariableDeclarator => ((IVariableDeclaratorOperation)lhs).Symbol, _ => lhs.GetReferencedMemberOrLocalOrParameter(), }; if (lhsSymbol is null) { return; } // handle target-typed new if (rhs is IConversionOperation { IsImplicit: true }) { rhs = rhs.WalkDownConversion(); } IMethodSymbol? rhsMethodSymbol = rhs.Kind switch { OperationKind.Invocation => ((IInvocationOperation)rhs).TargetMethod, OperationKind.ObjectCreation => ((IObjectCreationOperation)rhs).Constructor, _ => null, }; IPropertySymbol? rhsPropertySymbol = (rhs as IPropertyReferenceOperation)?.Property; if (rhsMethodSymbol.IsXsltSettingsCtor(_xmlTypes)) { XsltSettingsEnvironment env = new XsltSettingsEnvironment(); _xsltSettingsEnvironments[lhsSymbol] = env; env.XsltSettingsSymbol = lhsSymbol; env.XsltSettingsDefinitionSymbol = rhsMethodSymbol; env.XsltSettingsDefinition = lhs.Parent; env.EnclosingConstructSymbol = _enclosingSymbol; // default both properties are disabled env.IsDocumentFunctionDisabled = true; env.IsScriptDisabled = true; // XsltSettings Constructor (Boolean, Boolean) if (!rhsMethodSymbol.Parameters.IsEmpty) { var arguments = rhs.Kind switch { OperationKind.Invocation => ((IInvocationOperation)rhs).Arguments, OperationKind.ObjectCreation => ((IObjectCreationOperation)rhs).Arguments, _ => ImmutableArray<IArgumentOperation>.Empty, }; env.IsDocumentFunctionDisabled = arguments[0].Value.TryGetBoolConstantValue(out var value) && !value; env.IsScriptDisabled = arguments[1].Value.TryGetBoolConstantValue(out value) && !value; } var initializers = (rhs as IObjectCreationOperation)?.Initializer?.Initializers ?? ImmutableArray<IOperation>.Empty; foreach (var arg in initializers) { if (arg is not ISimpleAssignmentOperation assignment) { continue; } var argLhsPropertySymbol = (assignment.Target as IPropertyReferenceOperation)?.Property; var argRhs = assignment.Value; // anything other than a constant false is treated as true if (argLhsPropertySymbol.IsXsltSettingsEnableDocumentFunctionProperty(_xmlTypes)) { env.IsDocumentFunctionDisabled = argRhs.TryGetBoolConstantValue(out var value) && !value; } else if (argLhsPropertySymbol.IsXsltSettingsEnableScriptProperty(_xmlTypes)) { env.IsScriptDisabled = argRhs.TryGetBoolConstantValue(out var value) && !value; } } } else if (rhsPropertySymbol.IsXsltSettingsDefaultProperty(_xmlTypes)) { XsltSettingsEnvironment env = new XsltSettingsEnvironment(); _xsltSettingsEnvironments[lhsSymbol] = env; env.XsltSettingsSymbol = lhsSymbol; env.XsltSettingsDefinitionSymbol = rhsPropertySymbol; env.XsltSettingsDefinition = lhs.Parent; env.EnclosingConstructSymbol = _enclosingSymbol; env.IsDocumentFunctionDisabled = true; env.IsScriptDisabled = true; } else if (rhsPropertySymbol.IsXsltSettingsTrustedXsltProperty(_xmlTypes)) { XsltSettingsEnvironment env = new XsltSettingsEnvironment(); _xsltSettingsEnvironments[lhsSymbol] = env; env.XsltSettingsSymbol = lhsSymbol; env.XsltSettingsDefinitionSymbol = rhsPropertySymbol; env.XsltSettingsDefinition = lhs.Parent; env.EnclosingConstructSymbol = _enclosingSymbol; } else { var lhsPropertySymbol = lhsSymbol as IPropertySymbol; bool isXlstSettingsEnableDocumentFunctionProperty = lhsPropertySymbol.IsXsltSettingsEnableDocumentFunctionProperty(_xmlTypes); bool isXlstSettingsEnableScriptProperty = lhsPropertySymbol.IsXsltSettingsEnableScriptProperty(_xmlTypes); if (!isXlstSettingsEnableDocumentFunctionProperty && !isXlstSettingsEnableScriptProperty) { return; } IOperation? lhsExpression = (lhs as IMemberReferenceOperation)?.Instance; ISymbol? lhsExpressionSymbol = lhsExpression?.GetReferencedMemberOrLocalOrParameter(); if (lhsExpressionSymbol is null) { return; } if (!_xsltSettingsEnvironments.TryGetValue(lhsExpressionSymbol, out var env)) { env = new XsltSettingsEnvironment { XsltSettingsSymbol = lhsExpressionSymbol, }; _xsltSettingsEnvironments[lhsExpressionSymbol] = env; } if (isXlstSettingsEnableDocumentFunctionProperty) { env.IsDocumentFunctionDisabled = rhs.TryGetBoolConstantValue(out var value) && !value; } else if (isXlstSettingsEnableScriptProperty) { env.IsScriptDisabled = rhs.TryGetBoolConstantValue(out var value) && !value; } } } private class XsltSettingsEnvironment { internal ISymbol? XsltSettingsSymbol { get; set; } internal ISymbol? XsltSettingsDefinitionSymbol { get; set; } internal IOperation? XsltSettingsDefinition { get; set; } internal ISymbol? EnclosingConstructSymbol { get; set; } internal bool IsScriptDisabled { get; set; } internal bool IsDocumentFunctionDisabled { get; set; } } } } }