src/Analyzers/ExpressionTypeUsed.Analyzer.cs (231 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Immutable;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Azure.ApiManagement.PolicyToolkit.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class TypeUsedAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => Rules.TypeUsed.All;
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression,
SyntaxKind.SimpleMemberAccessExpression,
SyntaxKind.ElementAccessExpression,
SyntaxKind.ObjectCreationExpression,
SyntaxKind.ObjectInitializerExpression,
SyntaxKind.AnonymousObjectCreationExpression);
}
private readonly static IReadOnlySet<string> AllowedTypes = new HashSet<string>()
{
#region mslib
"System.Array",
"System.BitConverter",
"System.Boolean",
"System.Byte",
"System.Char",
"System.Collections.Generic.Dictionary<TKey, TValue>",
"System.Collections.Generic.HashSet<T>",
"System.Collections.Generic.ICollection<T>",
"System.Collections.Generic.IDictionary<TKey, TValue>",
"System.Collections.Generic.IEnumerable<T>",
"System.Collections.Generic.IEnumerator<T>",
"System.Collections.Generic.IList<T>",
"System.Collections.Generic.IReadOnlyCollection<T>",
"System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>",
"System.Collections.Generic.ISet<T>",
"System.Collections.Generic.KeyValuePair<TKey, TValue>",
"System.Collections.Generic.List<T>",
"System.Collections.Generic.Queue<T>",
"System.Collections.Generic.Stack<T>",
"System.Convert",
"System.DateTime",
"System.DateTimeKind",
"System.DateTimeOffset",
"System.Decimal",
"System.Double",
"System.Enum",
"System.Exception",
"System.Guid",
"System.Int16",
"System.Int32",
"System.Int64",
"System.IO.StringReader",
"System.IO.StringWriter",
"System.Linq.Enumerable",
"System.Math",
"System.MidpointRounding",
"System.Net.IPAddress",
"System.Net.WebUtility",
"System.Nullable",
"System.Object",
"System.Random",
"System.SByte",
"System.Security.Cryptography.AsymmetricAlgorithm",
"System.Security.Cryptography.CipherMode",
"System.Security.Cryptography.HashAlgorithm",
"System.Security.Cryptography.HashAlgorithmName",
"System.Security.Cryptography.HMAC",
"System.Security.Cryptography.HMACMD5",
"System.Security.Cryptography.HMACSHA1",
"System.Security.Cryptography.HMACSHA256",
"System.Security.Cryptography.HMACSHA384",
"System.Security.Cryptography.HMACSHA512",
"System.Security.Cryptography.KeyedHashAlgorithm",
"System.Security.Cryptography.MD5",
"System.Security.Cryptography.Oid",
"System.Security.Cryptography.PaddingMode",
"System.Security.Cryptography.RNGCryptoServiceProvider",
"System.Security.Cryptography.RSA",
"System.Security.Cryptography.RSAEncryptionPadding",
"System.Security.Cryptography.RSASignaturePadding",
"System.Security.Cryptography.SHA1",
"System.Security.Cryptography.SHA1Managed",
"System.Security.Cryptography.SHA256",
"System.Security.Cryptography.SHA256Managed",
"System.Security.Cryptography.SHA384",
"System.Security.Cryptography.SHA384Managed",
"System.Security.Cryptography.SHA512",
"System.Security.Cryptography.SHA512Managed",
"System.Security.Cryptography.SymmetricAlgorithm",
"System.Security.Cryptography.X509Certificates.PublicKey",
"System.Security.Cryptography.X509Certificates.RSACertificateExtensions",
"System.Security.Cryptography.X509Certificates.X500DistinguishedName",
"System.Security.Cryptography.X509Certificates.X509Certificate",
"System.Security.Cryptography.X509Certificates.X509Certificate2",
"System.Security.Cryptography.X509Certificates.X509ContentType",
"System.Security.Cryptography.X509Certificates.X509NameType",
"System.Single",
"System.String",
"System.StringComparer",
"System.StringComparison",
"System.StringSplitOptions",
"System.Text.Encoding",
"System.Text.RegularExpressions.Capture",
"System.Text.RegularExpressions.CaptureCollection",
"System.Text.RegularExpressions.Group",
"System.Text.RegularExpressions.GroupCollection",
"System.Text.RegularExpressions.Match",
"System.Text.RegularExpressions.Regex",
"System.Text.RegularExpressions.RegexOptions",
"System.Text.StringBuilder",
"System.TimeSpan",
"System.TimeZone",
"System.TimeZoneInfo.AdjustmentRule",
"System.TimeZoneInfo.TransitionTime",
"System.TimeZoneInfo",
"System.Tuple",
"System.UInt16",
"System.UInt32",
"System.UInt64",
"System.Uri",
"System.UriPartial",
"System.Xml.Linq.Extensions",
"System.Xml.Linq.XAttribute",
"System.Xml.Linq.XCData",
"System.Xml.Linq.XComment",
"System.Xml.Linq.XContainer",
"System.Xml.Linq.XDeclaration",
"System.Xml.Linq.XDocument",
"System.Xml.Linq.XDocumentType",
"System.Xml.Linq.XElement",
"System.Xml.Linq.XName",
"System.Xml.Linq.XNamespace",
"System.Xml.Linq.XNode",
"System.Xml.Linq.XNodeDocumentOrderComparer",
"System.Xml.Linq.XNodeEqualityComparer",
"System.Xml.Linq.XObject",
"System.Xml.Linq.XProcessingInstruction",
"System.Xml.Linq.XText",
"System.Xml.XmlNodeType",
#endregion mslib
#region Newtonsoft.Json
"Newtonsoft.Json.Formatting",
"Newtonsoft.Json.JsonConvert",
"Newtonsoft.Json.Linq.Extensions",
"Newtonsoft.Json.Linq.JArray",
"Newtonsoft.Json.Linq.JConstructor",
"Newtonsoft.Json.Linq.JContainer",
"Newtonsoft.Json.Linq.JObject",
"Newtonsoft.Json.Linq.JProperty",
"Newtonsoft.Json.Linq.JRaw",
"Newtonsoft.Json.Linq.JToken",
"Newtonsoft.Json.Linq.JTokenType",
"Newtonsoft.Json.Linq.JValue",
#endregion Newtonsoft.Json
#region Azure.ApiManagement.PolicyToolkit.Authoring.Expressions
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IApi",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IExpressionContext",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IContextApi",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IDeployment",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IGroup",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.ILastError",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IMessageBody",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IOperation",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IPrivateEndpointConnection",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IProduct",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IRequest",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IResponse",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.ISubscription",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.ISubscriptionKeyParameterNames",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IUrl",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IUser",
"Azure.ApiManagement.PolicyToolkit.Authoring.Expressions.IUserIdentity",
#endregion Azure.ApiManagement.PolicyToolkit.Authoring.Expressions
};
private readonly static IReadOnlyDictionary<string, IReadOnlySet<string>> AllowedInTypes = new Dictionary<string, IReadOnlySet<String>>()
{
{ "Newtonsoft.Json.JsonConvert", new HashSet<string>() { "SerializeObject", "DeserializeObject" } },
{ "System.DateTime", new HashSet<string>() { ".ctor", "Add", "AddDays", "AddHours", "AddMilliseconds", "AddMinutes", "AddMonths", "AddSeconds", "AddTicks", "AddYears", "Date", "Day", "DayOfWeek", "DayOfYear", "DaysInMonth", "Hour", "IsDaylightSavingTime", "IsLeapYear", "MaxValue", "Millisecond", "Minute", "MinValue", "Month", "Now", "Parse", "Second", "Subtract", "Ticks", "TimeOfDay", "Today", "ToString", "UtcNow", "Year" } },
{ "System.DateTimeKind", new HashSet<string>() { "Utc" } },
{ "System.Enum", new HashSet<string>() { "Parse", "TryParse", "ToString" } },
{ "System.Net.IPAddress", new HashSet<string>() { "AddressFamily", "Equals", "GetAddressBytes", "IsLoopback", "Parse", "TryParse", "ToString"} },
{ "System.Security.Cryptography.X509Certificates.X500DistinguishedName", new HashSet<string>() { "Name" } },
{ "System.Text.RegularExpressions.Capture", new HashSet<string>() { "Index", "Length", "Value" } },
{ "System.Text.RegularExpressions.CaptureCollection", new HashSet<string>() { "Count", "Item" } },
{ "System.Text.RegularExpressions.Group", new HashSet<string>() { "Captures", "Success" } },
{ "System.Text.RegularExpressions.GroupCollection", new HashSet<string>() { "Count", "Item" } },
{ "System.Text.RegularExpressions.Match", new HashSet<string>() { "Empty", "Groups", "Result" } },
{ "System.Text.RegularExpressions.Regex", new HashSet<string>() { ".ctor", "IsMatch", "Match", "Matches", "Replace", "Unescape", "Split" } },
};
private readonly static IReadOnlyDictionary<string, IReadOnlySet<string>> DisallowedInTypes = new Dictionary<string, IReadOnlySet<String>>()
{
{ "System.Xml.Linq.XDocument", new HashSet<string>() { "Load" } },
};
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var node = context.Node;
var underUnderExpressionMethod = node.IsPartOfPolicyExpressionMethod(context.SemanticModel);
var underUnderExpressionLambda = node.IsPartOfPolicyExpressionDelegate(context.SemanticModel);
if (!underUnderExpressionMethod && !underUnderExpressionLambda)
{
return;
}
var nodeSymbol = context.SemanticModel.GetSymbolInfo(node).Symbol;
if (nodeSymbol == null)
{
return;
}
var symbol = nodeSymbol.ContainingType;
var typeName = (symbol.IsGenericType ? symbol.OriginalDefinition : symbol)?.ToFullyQualifiedString() ?? "";
if (AllowedTypes.Contains(typeName))
{
if (AllowedInTypes.TryGetValue(typeName, out var allowed) && !allowed.Contains(nodeSymbol.Name))
{
context.ReportDiagnostic(Diagnostic.Create(Rules.TypeUsed.DisallowedMember, node.GetLocation(), nodeSymbol.Name));
}
else if (DisallowedInTypes.TryGetValue(typeName, out var disallowed) && disallowed.Contains(nodeSymbol.Name))
{
context.ReportDiagnostic(Diagnostic.Create(Rules.TypeUsed.DisallowedMember, node.GetLocation(), nodeSymbol.Name));
}
}
else
{
context.ReportDiagnostic(Diagnostic.Create(Rules.TypeUsed.DisallowedType, node.GetLocation(), typeName));
}
}
}