| File: System\CodeDom\Compiler\CodeValidator.cs | Web Access |
| Project: src\runtime\src\libraries\System.CodeDom\src\System.CodeDom.csproj (System.CodeDom) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.IO; namespace System.CodeDom.Compiler { // This is an internal helper class which walks the tree for the ValidateIdentifiers API in the CodeGenerator. For the most part the generator code has been copied and // turned into validation code. This code will only validate identifiers and types to check that they are ok in a language // independent manner. By default, this will not be turned on. This gives clients of codedom a mechanism to // protect themselves against certain types of code injection attacks (using identifier and type names). // You can pass in any node in the tree that is a subclass of CodeObject. internal sealed class CodeValidator { private static readonly SearchValues<char> s_newLineChars = SearchValues.Create("\r\n\u2028\u2029\u0085"); private static readonly SearchValues<char> s_invalidPathChars = SearchValues.Create(Path.GetInvalidPathChars()); private CodeTypeDeclaration _currentClass; internal void ValidateIdentifiers(CodeObject e) { if (e is CodeCompileUnit) { ValidateCodeCompileUnit((CodeCompileUnit)e); } else if (e is CodeComment) { // do nothing } else if (e is CodeExpression) { ValidateExpression((CodeExpression)e); } else if (e is CodeNamespace) { ValidateNamespace((CodeNamespace)e); } else if (e is CodeNamespaceImport) { ValidateNamespaceImport((CodeNamespaceImport)e); } else if (e is CodeStatement) { ValidateStatement((CodeStatement)e); } else if (e is CodeTypeMember) { ValidateTypeMember((CodeTypeMember)e); } else if (e is CodeTypeReference) { ValidateTypeReference((CodeTypeReference)e); } else if (e is CodeDirective) { ValidateCodeDirective((CodeDirective)e); } else { ArgumentNullException.ThrowIfNull(e); throw new ArgumentException(SR.Format(SR.InvalidElementType, e.GetType().FullName), nameof(e)); } } private void ValidateTypeMember(CodeTypeMember e) { ValidateCodeDirectives(e.StartDirectives); ValidateCodeDirectives(e.EndDirectives); if (e is CodeMemberEvent) { ValidateEvent((CodeMemberEvent)e); } else if (e is CodeMemberField) { ValidateField((CodeMemberField)e); } else if (e is CodeMemberMethod) { ValidateMemberMethod((CodeMemberMethod)e); } else if (e is CodeMemberProperty) { ValidateProperty((CodeMemberProperty)e); } else if (e is CodeSnippetTypeMember) { // do nothing } else if (e is CodeTypeDeclaration) { ValidateTypeDeclaration((CodeTypeDeclaration)e); } else { throw new ArgumentException(SR.Format(SR.InvalidElementType, e.GetType().FullName), nameof(e)); } } private void ValidateCodeCompileUnit(CodeCompileUnit e) { ValidateCodeDirectives(e.StartDirectives); ValidateCodeDirectives(e.EndDirectives); if (e is CodeSnippetCompileUnit) { // do nothing } else { ValidateCompileUnitStart(e); ValidateNamespaces(e); } } private void ValidateCompileUnitStart(CodeCompileUnit e) { if (e.AssemblyCustomAttributes.Count > 0) { ValidateAttributes(e.AssemblyCustomAttributes); } } private void ValidateNamespaces(CodeCompileUnit e) { foreach (CodeNamespace n in e.Namespaces) { ValidateNamespace(n); } } private void ValidateNamespace(CodeNamespace e) { ValidateNamespaceStart(e); ValidateNamespaceImports(e); ValidateTypes(e); } private static void ValidateNamespaceStart(CodeNamespace e) { if (!string.IsNullOrEmpty(e.Name)) { ValidateTypeName(e, nameof(e.Name), e.Name); } } private static void ValidateNamespaceImports(CodeNamespace e) { foreach (CodeNamespaceImport imp in e.Imports) { ValidateNamespaceImport(imp); } } private static void ValidateNamespaceImport(CodeNamespaceImport e) { ValidateTypeName(e, nameof(e.Namespace), e.Namespace); } private void ValidateAttributes(CodeAttributeDeclarationCollection attributes) { if (attributes.Count == 0) return; foreach (CodeAttributeDeclaration current in attributes) { ValidateTypeName(current, nameof(current.Name), current.Name); ValidateTypeReference(current.AttributeType); foreach (CodeAttributeArgument arg in current.Arguments) { ValidateAttributeArgument(arg); } } } private void ValidateAttributeArgument(CodeAttributeArgument arg) { if (!string.IsNullOrEmpty(arg.Name)) { ValidateIdentifier(arg, nameof(arg.Name), arg.Name); } ValidateExpression(arg.Value); } private void ValidateTypes(CodeNamespace e) { foreach (CodeTypeDeclaration type in e.Types) { ValidateTypeDeclaration(type); } } private void ValidateTypeDeclaration(CodeTypeDeclaration e) { // This function can be called recursively and will modify the global variable currentClass // We will save currentClass to a local, modify it to do whatever we want and restore it back when we exit so that it is re-entrant. CodeTypeDeclaration savedClass = _currentClass; _currentClass = e; ValidateTypeStart(e); ValidateTypeParameters(e.TypeParameters); ValidateTypeMembers(e); // Recursive call can come from here. ValidateTypeReferences(e.BaseTypes); _currentClass = savedClass; } private void ValidateTypeMembers(CodeTypeDeclaration e) { foreach (CodeTypeMember currentMember in e.Members) { ValidateTypeMember(currentMember); } } private void ValidateTypeParameters(CodeTypeParameterCollection parameters) { for (int i = 0; i < parameters.Count; i++) { ValidateTypeParameter(parameters[i]); } } private void ValidateTypeParameter(CodeTypeParameter e) { ValidateIdentifier(e, nameof(e.Name), e.Name); ValidateTypeReferences(e.Constraints); ValidateAttributes(e.CustomAttributes); } private void ValidateField(CodeMemberField e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } ValidateIdentifier(e, nameof(e.Name), e.Name); if (!IsCurrentEnum) { ValidateTypeReference(e.Type); } if (e.InitExpression != null) { ValidateExpression(e.InitExpression); } } private void ValidateConstructor(CodeConstructor e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } ValidateParameters(e.Parameters); CodeExpressionCollection baseArgs = e.BaseConstructorArgs; CodeExpressionCollection thisArgs = e.ChainedConstructorArgs; if (baseArgs.Count > 0) { ValidateExpressionList(baseArgs); } if (thisArgs.Count > 0) { ValidateExpressionList(thisArgs); } ValidateStatements(e.Statements); } private void ValidateProperty(CodeMemberProperty e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } ValidateTypeReference(e.Type); ValidateTypeReferences(e.ImplementationTypes); if (e.PrivateImplementationType != null && !IsCurrentInterface) { ValidateTypeReference(e.PrivateImplementationType); } if (e.Parameters.Count > 0 && string.Equals(e.Name, "Item", StringComparison.OrdinalIgnoreCase)) { ValidateParameters(e.Parameters); } else { ValidateIdentifier(e, nameof(e.Name), e.Name); } if (e.HasGet) { if (!(IsCurrentInterface || (e.Attributes & MemberAttributes.ScopeMask) == MemberAttributes.Abstract)) { ValidateStatements(e.GetStatements); } } if (e.HasSet) { if (!(IsCurrentInterface || (e.Attributes & MemberAttributes.ScopeMask) == MemberAttributes.Abstract)) { ValidateStatements(e.SetStatements); } } } private void ValidateMemberMethod(CodeMemberMethod e) { ValidateTypeParameters(e.TypeParameters); ValidateTypeReferences(e.ImplementationTypes); if (e is CodeEntryPointMethod) { ValidateStatements(((CodeEntryPointMethod)e).Statements); } else if (e is CodeConstructor) { ValidateConstructor((CodeConstructor)e); } else if (e is CodeTypeConstructor) { ValidateTypeConstructor((CodeTypeConstructor)e); } else { ValidateMethod(e); } } private void ValidateTypeConstructor(CodeTypeConstructor e) { ValidateStatements(e.Statements); } private void ValidateMethod(CodeMemberMethod e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } if (e.ReturnTypeCustomAttributes.Count > 0) { ValidateAttributes(e.ReturnTypeCustomAttributes); } ValidateTypeReference(e.ReturnType); if (e.PrivateImplementationType != null) { ValidateTypeReference(e.PrivateImplementationType); } ValidateIdentifier(e, nameof(e.Name), e.Name); ValidateParameters(e.Parameters); if (!IsCurrentInterface && (e.Attributes & MemberAttributes.ScopeMask) != MemberAttributes.Abstract) { ValidateStatements(e.Statements); } } private void ValidateTypeStart(CodeTypeDeclaration e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } ValidateIdentifier(e, nameof(e.Name), e.Name); if (e is CodeTypeDelegate del) { ValidateTypeReference(del.ReturnType); ValidateParameters(del.Parameters); } else { foreach (CodeTypeReference typeRef in e.BaseTypes) { ValidateTypeReference(typeRef); } } } private void ValidateStatement(CodeStatement e) { ArgumentNullException.ThrowIfNull(e); ValidateCodeDirectives(e.StartDirectives); ValidateCodeDirectives(e.EndDirectives); if (e is CodeCommentStatement) { // nothing } else if (e is CodeMethodReturnStatement) { ValidateMethodReturnStatement((CodeMethodReturnStatement)e); } else if (e is CodeConditionStatement) { ValidateConditionStatement((CodeConditionStatement)e); } else if (e is CodeTryCatchFinallyStatement) { ValidateTryCatchFinallyStatement((CodeTryCatchFinallyStatement)e); } else if (e is CodeAssignStatement) { ValidateAssignStatement((CodeAssignStatement)e); } else if (e is CodeExpressionStatement) { ValidateExpressionStatement((CodeExpressionStatement)e); } else if (e is CodeIterationStatement) { ValidateIterationStatement((CodeIterationStatement)e); } else if (e is CodeThrowExceptionStatement) { ValidateThrowExceptionStatement((CodeThrowExceptionStatement)e); } else if (e is CodeSnippetStatement) { // do nothing } else if (e is CodeVariableDeclarationStatement) { ValidateVariableDeclarationStatement((CodeVariableDeclarationStatement)e); } else if (e is CodeAttachEventStatement) { ValidateAttachEventStatement((CodeAttachEventStatement)e); } else if (e is CodeRemoveEventStatement) { ValidateRemoveEventStatement((CodeRemoveEventStatement)e); } else if (e is CodeGotoStatement) { ValidateGotoStatement((CodeGotoStatement)e); } else if (e is CodeLabeledStatement) { ValidateLabeledStatement((CodeLabeledStatement)e); } else { throw new ArgumentException(SR.Format(SR.InvalidElementType, e.GetType().FullName), nameof(e)); } } private void ValidateStatements(CodeStatementCollection stmts) { foreach (CodeStatement stmt in stmts) { ValidateStatement(stmt); } } private void ValidateExpressionStatement(CodeExpressionStatement e) { ValidateExpression(e.Expression); } private void ValidateIterationStatement(CodeIterationStatement e) { ValidateStatement(e.InitStatement); ValidateExpression(e.TestExpression); ValidateStatement(e.IncrementStatement); ValidateStatements(e.Statements); } private void ValidateThrowExceptionStatement(CodeThrowExceptionStatement e) { if (e.ToThrow != null) { ValidateExpression(e.ToThrow); } } private void ValidateMethodReturnStatement(CodeMethodReturnStatement e) { if (e.Expression != null) { ValidateExpression(e.Expression); } } private void ValidateConditionStatement(CodeConditionStatement e) { ValidateExpression(e.Condition); ValidateStatements(e.TrueStatements); CodeStatementCollection falseStatements = e.FalseStatements; if (falseStatements.Count > 0) { ValidateStatements(e.FalseStatements); } } private void ValidateTryCatchFinallyStatement(CodeTryCatchFinallyStatement e) { ValidateStatements(e.TryStatements); CodeCatchClauseCollection catches = e.CatchClauses; if (catches.Count > 0) { foreach (CodeCatchClause current in catches) { ValidateTypeReference(current.CatchExceptionType); ValidateIdentifier(current, nameof(current.LocalName), current.LocalName); ValidateStatements(current.Statements); } } CodeStatementCollection finallyStatements = e.FinallyStatements; if (finallyStatements.Count > 0) { ValidateStatements(finallyStatements); } } private void ValidateAssignStatement(CodeAssignStatement e) { ValidateExpression(e.Left); ValidateExpression(e.Right); } private void ValidateAttachEventStatement(CodeAttachEventStatement e) { ValidateEventReferenceExpression(e.Event); ValidateExpression(e.Listener); } private void ValidateRemoveEventStatement(CodeRemoveEventStatement e) { ValidateEventReferenceExpression(e.Event); ValidateExpression(e.Listener); } private static void ValidateGotoStatement(CodeGotoStatement e) { ValidateIdentifier(e, nameof(e.Label), e.Label); } private void ValidateLabeledStatement(CodeLabeledStatement e) { ValidateIdentifier(e, nameof(e.Label), e.Label); if (e.Statement != null) { ValidateStatement(e.Statement); } } private void ValidateVariableDeclarationStatement(CodeVariableDeclarationStatement e) { ValidateTypeReference(e.Type); ValidateIdentifier(e, nameof(e.Name), e.Name); if (e.InitExpression != null) { ValidateExpression(e.InitExpression); } } private void ValidateEvent(CodeMemberEvent e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } if (e.PrivateImplementationType != null) { ValidateTypeReference(e.Type); ValidateIdentifier(e, nameof(e.Name), e.Name); } ValidateTypeReferences(e.ImplementationTypes); } private void ValidateParameters(CodeParameterDeclarationExpressionCollection parameters) { foreach (CodeParameterDeclarationExpression current in parameters) { ValidateParameterDeclarationExpression(current); } } private void ValidateExpressionList(CodeExpressionCollection expressions) { foreach (CodeExpression current in expressions) { ValidateExpression(current); } } private static void ValidateTypeReference(CodeTypeReference e) { ValidateTypeName(e, nameof(e.BaseType), e.BaseType); ValidateArity(e); ValidateTypeReferences(e.TypeArguments); } private static void ValidateTypeReferences(CodeTypeReferenceCollection refs) { for (int i = 0; i < refs.Count; i++) { ValidateTypeReference(refs[i]); } } private static void ValidateArity(CodeTypeReference e) { // Verify that the number of TypeArguments agrees with the arity on the type. string baseType = e.BaseType; int totalTypeArgs = 0; for (int i = 0; i < baseType.Length; i++) { if (baseType[i] == '`') { i++; // skip the ' int numTypeArgs = 0; while (i < baseType.Length && baseType[i] >= '0' && baseType[i] <= '9') { numTypeArgs = numTypeArgs * 10 + (baseType[i] - '0'); i++; } totalTypeArgs += numTypeArgs; } } // Check if we have zero type args for open types. if ((totalTypeArgs != e.TypeArguments.Count) && (e.TypeArguments.Count != 0)) { throw new ArgumentException(SR.Format(SR.ArityDoesntMatch, baseType, e.TypeArguments.Count), nameof(e)); } } private static void ValidateTypeName(object e, string propertyName, string typeName) { if (!CodeGenerator.IsValidLanguageIndependentTypeName(typeName)) { string message = SR.Format(SR.InvalidTypeName, typeName, propertyName, e.GetType().FullName); throw new ArgumentException(message, nameof(e)); } } private static void ValidateIdentifier(object e, string propertyName, string identifier) { if (!CodeGenerator.IsValidLanguageIndependentIdentifier(identifier)) { string message = SR.Format(SR.InvalidLanguageIdentifier, identifier, propertyName, e.GetType().FullName); throw new ArgumentException(message, nameof(e)); } } private void ValidateExpression(CodeExpression e) { if (e is CodeArrayCreateExpression) { ValidateArrayCreateExpression((CodeArrayCreateExpression)e); } else if (e is CodeBaseReferenceExpression) { // Nothing to validate } else if (e is CodeBinaryOperatorExpression) { ValidateBinaryOperatorExpression((CodeBinaryOperatorExpression)e); } else if (e is CodeCastExpression) { ValidateCastExpression((CodeCastExpression)e); } else if (e is CodeDefaultValueExpression) { ValidateDefaultValueExpression((CodeDefaultValueExpression)e); } else if (e is CodeDelegateCreateExpression) { ValidateDelegateCreateExpression((CodeDelegateCreateExpression)e); } else if (e is CodeFieldReferenceExpression) { ValidateFieldReferenceExpression((CodeFieldReferenceExpression)e); } else if (e is CodeArgumentReferenceExpression) { ValidateArgumentReferenceExpression((CodeArgumentReferenceExpression)e); } else if (e is CodeVariableReferenceExpression) { ValidateVariableReferenceExpression((CodeVariableReferenceExpression)e); } else if (e is CodeIndexerExpression) { ValidateIndexerExpression((CodeIndexerExpression)e); } else if (e is CodeArrayIndexerExpression) { ValidateArrayIndexerExpression((CodeArrayIndexerExpression)e); } else if (e is CodeSnippetExpression) { // do nothing } else if (e is CodeMethodInvokeExpression) { ValidateMethodInvokeExpression((CodeMethodInvokeExpression)e); } else if (e is CodeMethodReferenceExpression) { ValidateMethodReferenceExpression((CodeMethodReferenceExpression)e); } else if (e is CodeEventReferenceExpression) { ValidateEventReferenceExpression((CodeEventReferenceExpression)e); } else if (e is CodeDelegateInvokeExpression) { ValidateDelegateInvokeExpression((CodeDelegateInvokeExpression)e); } else if (e is CodeObjectCreateExpression) { ValidateObjectCreateExpression((CodeObjectCreateExpression)e); } else if (e is CodeParameterDeclarationExpression) { ValidateParameterDeclarationExpression((CodeParameterDeclarationExpression)e); } else if (e is CodeDirectionExpression) { ValidateDirectionExpression((CodeDirectionExpression)e); } else if (e is CodePrimitiveExpression) { // do nothing } else if (e is CodePropertyReferenceExpression) { ValidatePropertyReferenceExpression((CodePropertyReferenceExpression)e); } else if (e is CodePropertySetValueReferenceExpression) { // Do nothing } else if (e is CodeThisReferenceExpression) { // Do nothing } else if (e is CodeTypeReferenceExpression) { ValidateTypeReference(((CodeTypeReferenceExpression)e).Type); } else if (e is CodeTypeOfExpression) { ValidateTypeOfExpression((CodeTypeOfExpression)e); } else { ArgumentNullException.ThrowIfNull(e); throw new ArgumentException(SR.Format(SR.InvalidElementType, e.GetType().FullName), nameof(e)); } } private void ValidateArrayCreateExpression(CodeArrayCreateExpression e) { ValidateTypeReference(e.CreateType); CodeExpressionCollection init = e.Initializers; if (init.Count > 0) { ValidateExpressionList(init); } else { if (e.SizeExpression != null) { ValidateExpression(e.SizeExpression); } } } private void ValidateBinaryOperatorExpression(CodeBinaryOperatorExpression e) { ValidateExpression(e.Left); ValidateExpression(e.Right); } private void ValidateCastExpression(CodeCastExpression e) { ValidateTypeReference(e.TargetType); ValidateExpression(e.Expression); } private static void ValidateDefaultValueExpression(CodeDefaultValueExpression e) { ValidateTypeReference(e.Type); } private void ValidateDelegateCreateExpression(CodeDelegateCreateExpression e) { ValidateTypeReference(e.DelegateType); ValidateExpression(e.TargetObject); ValidateIdentifier(e, nameof(e.MethodName), e.MethodName); } private void ValidateFieldReferenceExpression(CodeFieldReferenceExpression e) { if (e.TargetObject != null) { ValidateExpression(e.TargetObject); } ValidateIdentifier(e, nameof(e.FieldName), e.FieldName); } private static void ValidateArgumentReferenceExpression(CodeArgumentReferenceExpression e) { ValidateIdentifier(e, nameof(e.ParameterName), e.ParameterName); } private static void ValidateVariableReferenceExpression(CodeVariableReferenceExpression e) { ValidateIdentifier(e, nameof(e.VariableName), e.VariableName); } private void ValidateIndexerExpression(CodeIndexerExpression e) { ValidateExpression(e.TargetObject); foreach (CodeExpression exp in e.Indices) { ValidateExpression(exp); } } private void ValidateArrayIndexerExpression(CodeArrayIndexerExpression e) { ValidateExpression(e.TargetObject); foreach (CodeExpression exp in e.Indices) { ValidateExpression(exp); } } private void ValidateMethodInvokeExpression(CodeMethodInvokeExpression e) { ValidateMethodReferenceExpression(e.Method); ValidateExpressionList(e.Parameters); } private void ValidateMethodReferenceExpression(CodeMethodReferenceExpression e) { if (e.TargetObject != null) { ValidateExpression(e.TargetObject); } ValidateIdentifier(e, nameof(e.MethodName), e.MethodName); ValidateTypeReferences(e.TypeArguments); } private void ValidateEventReferenceExpression(CodeEventReferenceExpression e) { if (e.TargetObject != null) { ValidateExpression(e.TargetObject); } ValidateIdentifier(e, nameof(e.EventName), e.EventName); } private void ValidateDelegateInvokeExpression(CodeDelegateInvokeExpression e) { if (e.TargetObject != null) { ValidateExpression(e.TargetObject); } ValidateExpressionList(e.Parameters); } private void ValidateObjectCreateExpression(CodeObjectCreateExpression e) { ValidateTypeReference(e.CreateType); ValidateExpressionList(e.Parameters); } private void ValidateParameterDeclarationExpression(CodeParameterDeclarationExpression e) { if (e.CustomAttributes.Count > 0) { ValidateAttributes(e.CustomAttributes); } ValidateTypeReference(e.Type); ValidateIdentifier(e, nameof(e.Name), e.Name); } private void ValidateDirectionExpression(CodeDirectionExpression e) { ValidateExpression(e.Expression); } private void ValidatePropertyReferenceExpression(CodePropertyReferenceExpression e) { if (e.TargetObject != null) { ValidateExpression(e.TargetObject); } ValidateIdentifier(e, nameof(e.PropertyName), e.PropertyName); } private static void ValidateTypeOfExpression(CodeTypeOfExpression e) { ValidateTypeReference(e.Type); } private static void ValidateCodeDirectives(CodeDirectiveCollection e) { for (int i = 0; i < e.Count; i++) ValidateCodeDirective(e[i]); } private static void ValidateCodeDirective(CodeDirective e) { if (e is CodeChecksumPragma) { ValidateChecksumPragma((CodeChecksumPragma)e); } else if (e is CodeRegionDirective) { ValidateRegionDirective((CodeRegionDirective)e); } else { throw new ArgumentException(SR.Format(SR.InvalidElementType, e.GetType().FullName), nameof(e)); } } private static void ValidateChecksumPragma(CodeChecksumPragma e) { if (e.FileName.AsSpan().ContainsAny(s_invalidPathChars)) throw new ArgumentException(SR.Format(SR.InvalidPathCharsInChecksum, e.FileName), nameof(e)); } private static void ValidateRegionDirective(CodeRegionDirective e) { if (e.RegionText.AsSpan().ContainsAny(s_newLineChars)) throw new ArgumentException(SR.Format(SR.InvalidRegion, e.RegionText), nameof(e)); } private bool IsCurrentInterface => _currentClass != null && !(_currentClass is CodeTypeDelegate) ? _currentClass.IsInterface : false; private bool IsCurrentEnum => _currentClass != null && !(_currentClass is CodeTypeDelegate) ? _currentClass.IsEnum : false; } }