From 94314f3ab0570d8e8aaa8ea7723c2b41d23257df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:06:39 +0000 Subject: [PATCH 1/7] Initial plan From 95d3375fc2ba9f616377619867148c54b8692cf5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:23:29 +0000 Subject: [PATCH 2/7] Add C# reflection detection rules Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- .../RuleProcessor/ReflectionTests.cs | 264 ++++++++++++++++++ AppInspector/rules/default/os/reflection.json | 212 ++++++++++++++ nuget.config | 1 + 3 files changed, 477 insertions(+) create mode 100644 AppInspector.Tests/RuleProcessor/ReflectionTests.cs create mode 100644 AppInspector/rules/default/os/reflection.json diff --git a/AppInspector.Tests/RuleProcessor/ReflectionTests.cs b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs new file mode 100644 index 00000000..0b73485b --- /dev/null +++ b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs @@ -0,0 +1,264 @@ +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.ApplicationInspector.RulesEngine; +using Xunit; + +namespace AppInspector.Tests.RuleProcessor; + +public class ReflectionTests +{ + private readonly Languages _languages = new(); + + [Fact] + public void DetectMethodInfoInvoke() + { + var testCode = @" +using System; +using System.Reflection; + +class Program +{ + static void Main() + { + Type type = typeof(MyClass); + MethodInfo method = type.GetMethod(""MyMethod""); + method.Invoke(obj, new object[] { }); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var methodInvokeMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.MethodInvocation") ?? false).ToList(); + Assert.NotEmpty(methodInvokeMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectConstructorInfoInvoke() + { + var testCode = @" +using System; +using System.Reflection; + +class Program +{ + static void Main() + { + Type type = typeof(MyClass); + ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); + object instance = constructor.Invoke(null); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var constructorInvokeMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.ConstructorInvocation") ?? false).ToList(); + Assert.NotEmpty(constructorInvokeMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectAssemblyLoad() + { + var testCode = @" +using System; +using System.Reflection; + +class Program +{ + static void Main() + { + Assembly assembly1 = Assembly.Load(""MyAssembly""); + Assembly assembly2 = Assembly.LoadFrom(""path/to/assembly.dll""); + Assembly assembly3 = Assembly.LoadFile(""C:\\path\\to\\assembly.dll""); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var assemblyLoadMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.AssemblyLoading") ?? false).ToList(); + Assert.NotEmpty(assemblyLoadMatches); + // Should detect all three: Load, LoadFrom, LoadFile + Assert.True(assemblyLoadMatches.Count >= 1); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectInvokeMember() + { + var testCode = @" +using System; +using System.Reflection; + +class Program +{ + static void Main() + { + Type type = typeof(MyClass); + object result = type.InvokeMember(""MyMethod"", + BindingFlags.InvokeMethod, null, obj, new object[] { }); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var invokeMemberMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.InvokeMember") ?? false).ToList(); + Assert.NotEmpty(invokeMemberMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectActivatorCreateInstance() + { + var testCode = @" +using System; + +class Program +{ + static void Main() + { + object instance1 = Activator.CreateInstance(typeof(MyClass)); + object instance2 = Activator.CreateInstance(""MyAssembly"", ""MyNamespace.MyClass""); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var createInstanceMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.CreateInstance") ?? false).ToList(); + Assert.NotEmpty(createInstanceMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectGetMethod() + { + var testCode = @" +using System; +using System.Reflection; + +class Program +{ + static void Main() + { + Type type = typeof(MyClass); + MethodInfo method = type.GetMethod(""MyMethod""); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var getMethodMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.GetMethod") ?? false).ToList(); + Assert.NotEmpty(getMethodMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void DetectGetType() + { + var testCode = @" +using System; + +class Program +{ + static void Main() + { + Type type = Type.GetType(""System.String""); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var getTypeMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.GetType") ?? false).ToList(); + Assert.NotEmpty(getTypeMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } + + [Fact] + public void NoFalsePositiveOnNonReflectionCode() + { + var testCode = @" +using System; + +class Program +{ + static void Main() + { + Console.WriteLine(""Hello World""); + var myClass = new MyClass(); + myClass.DoSomething(); + } +}"; + RuleSet rules = new(); + rules.AddDirectory("/home/runner/work/ApplicationInspector/ApplicationInspector/AppInspector/rules/default/os"); + var processor = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions()); + + if (_languages.FromFileNameOut("test.cs", out var info)) + { + var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); + var reflectionMatches = matches.Where(m => + m.Tags?.Any(tag => tag.Contains("OS.Reflection")) ?? false).ToList(); + Assert.Empty(reflectionMatches); + } + else + { + Assert.Fail("Failed to get language info"); + } + } +} diff --git a/AppInspector/rules/default/os/reflection.json b/AppInspector/rules/default/os/reflection.json new file mode 100644 index 00000000..bc4696d9 --- /dev/null +++ b/AppInspector/rules/default/os/reflection.json @@ -0,0 +1,212 @@ +[ + { + "name": "OS: Reflection - Method Invocation", + "id": "AI036000", + "description": "Detects dynamic method invocation via reflection which can enable runtime behavior not visible at static analysis time", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.MethodInvocation" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "MethodInfo|MethodBase", + "type": "regexword", + "scopes": [ + "code" + ], + "confidence": "medium", + "_comment": "Detects MethodInfo and MethodBase usage for reflection" + } + ], + "conditions": [ + { + "pattern": { + "pattern": "\\.Invoke\\(", + "type": "regex", + "scopes": [ + "code" + ] + }, + "search_in": "same-line", + "_comment": "Ensures .Invoke( is on the same line to confirm method invocation" + } + ] + }, + { + "name": "OS: Reflection - Constructor Invocation", + "id": "AI036100", + "description": "Detects dynamic object creation via reflection using ConstructorInfo", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.ConstructorInvocation" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "ConstructorInfo", + "type": "regexword", + "scopes": [ + "code" + ], + "confidence": "medium", + "_comment": "Detects ConstructorInfo usage for reflection" + } + ], + "conditions": [ + { + "pattern": { + "pattern": "\\.Invoke\\(", + "type": "regex", + "scopes": [ + "code" + ] + }, + "search_in": "same-line", + "_comment": "Ensures .Invoke( is on the same line to confirm constructor invocation" + } + ] + }, + { + "name": "OS: Reflection - Dynamic Assembly Loading", + "id": "AI036200", + "description": "Detects dynamic assembly loading at runtime using Assembly.Load which can introduce code not visible at static analysis time", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.AssemblyLoading" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "Assembly\\.Load\\b", + "type": "regex", + "scopes": [ + "code" + ], + "confidence": "high", + "_comment": "Detects Assembly.Load - note LoadFile and LoadFrom are already in load_dll.json" + } + ] + }, + { + "name": "OS: Reflection - InvokeMember", + "id": "AI036300", + "description": "Detects Type.InvokeMember which enables dynamic member access via reflection", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.InvokeMember" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "\\.InvokeMember\\(", + "type": "regex", + "scopes": [ + "code" + ], + "confidence": "high", + "_comment": "Detects .InvokeMember( for dynamic member access" + } + ] + }, + { + "name": "OS: Reflection - Activator.CreateInstance", + "id": "AI036400", + "description": "Detects dynamic object creation using Activator.CreateInstance", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.CreateInstance" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "Activator\\.CreateInstance", + "type": "regexword", + "scopes": [ + "code" + ], + "confidence": "high", + "_comment": "Detects Activator.CreateInstance for dynamic object instantiation" + } + ] + }, + { + "name": "OS: Reflection - GetMethod", + "id": "AI036500", + "description": "Detects retrieval of method information via reflection", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.GetMethod" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "\\.GetMethod\\(", + "type": "regex", + "scopes": [ + "code" + ], + "confidence": "medium", + "_comment": "Detects .GetMethod( which is often used before dynamic invocation" + } + ] + }, + { + "name": "OS: Reflection - GetConstructor", + "id": "AI036600", + "description": "Detects retrieval of constructor information via reflection", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.GetConstructor" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "\\.GetConstructor\\(", + "type": "regex", + "scopes": [ + "code" + ], + "confidence": "medium", + "_comment": "Detects .GetConstructor( which is often used before dynamic object creation" + } + ] + }, + { + "name": "OS: Reflection - Dynamic Type Loading", + "id": "AI036700", + "description": "Detects dynamic type loading using Type.GetType", + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Reflection.GetType" + ], + "severity": "moderate", + "patterns": [ + { + "pattern": "Type\\.GetType\\(", + "type": "regex", + "scopes": [ + "code" + ], + "confidence": "medium", + "_comment": "Detects Type.GetType( for dynamic type loading from string" + } + ] + } +] diff --git a/nuget.config b/nuget.config index be6dddfa..9c5b211d 100644 --- a/nuget.config +++ b/nuget.config @@ -3,5 +3,6 @@ + \ No newline at end of file From b441b5d1ac0ad873dd3bdc01036d2256ca011464 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:23:44 +0000 Subject: [PATCH 3/7] Revert nuget.config changes Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- nuget.config | 1 - 1 file changed, 1 deletion(-) diff --git a/nuget.config b/nuget.config index 9c5b211d..be6dddfa 100644 --- a/nuget.config +++ b/nuget.config @@ -3,6 +3,5 @@ - \ No newline at end of file From e23ee8a0e0ef2a1966d6fc0287249e364ff9d084 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:24:38 +0000 Subject: [PATCH 4/7] Address code review feedback Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- AppInspector.Tests/RuleProcessor/ReflectionTests.cs | 3 +-- AppInspector/rules/default/os/reflection.json | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AppInspector.Tests/RuleProcessor/ReflectionTests.cs b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs index 0b73485b..b87bb4b9 100644 --- a/AppInspector.Tests/RuleProcessor/ReflectionTests.cs +++ b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs @@ -98,9 +98,8 @@ static void Main() { var matches = processor.AnalyzeFile(testCode, new FileEntry("test.cs", new MemoryStream()), info); var assemblyLoadMatches = matches.Where(m => m.Tags?.Contains("OS.Reflection.AssemblyLoading") ?? false).ToList(); + // Should detect Assembly.Load (LoadFrom and LoadFile are in load_dll.json) Assert.NotEmpty(assemblyLoadMatches); - // Should detect all three: Load, LoadFrom, LoadFile - Assert.True(assemblyLoadMatches.Count >= 1); } else { diff --git a/AppInspector/rules/default/os/reflection.json b/AppInspector/rules/default/os/reflection.json index bc4696d9..c83b67a5 100644 --- a/AppInspector/rules/default/os/reflection.json +++ b/AppInspector/rules/default/os/reflection.json @@ -84,13 +84,13 @@ "severity": "moderate", "patterns": [ { - "pattern": "Assembly\\.Load\\b", + "pattern": "Assembly\\.Load\\(", "type": "regex", "scopes": [ "code" ], "confidence": "high", - "_comment": "Detects Assembly.Load - note LoadFile and LoadFrom are already in load_dll.json" + "_comment": "Detects Assembly.Load( - note LoadFile and LoadFrom are already in load_dll.json" } ] }, From 31511d76c9f93431e74b62cd1145e28541728c94 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:39:22 +0000 Subject: [PATCH 5/7] Add must-match and must-not-match self-tests to reflection rules Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- AppInspector/rules/default/os/reflection.json | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/AppInspector/rules/default/os/reflection.json b/AppInspector/rules/default/os/reflection.json index c83b67a5..ce1efcbc 100644 --- a/AppInspector/rules/default/os/reflection.json +++ b/AppInspector/rules/default/os/reflection.json @@ -33,6 +33,17 @@ "search_in": "same-line", "_comment": "Ensures .Invoke( is on the same line to confirm method invocation" } + ], + "must-match": [ + "MethodInfo method = type.GetMethod(\"Execute\"); method.Invoke(instance, null);", + "MethodBase baseMethod = typeof(MyClass).GetMethod(\"Test\"); var result = baseMethod.Invoke(obj, new object[] { });", + "MethodInfo mi = GetMethodInfo(); mi.Invoke(target, args);" + ], + "must-not-match": [ + "MethodInfo method = type.GetMethod(\"Execute\");", + "var info = typeof(String).GetMethod(\"ToUpper\");", + "public void MyMethod() { }", + "method.InvokeAsync(args);" ] }, { @@ -69,6 +80,17 @@ "search_in": "same-line", "_comment": "Ensures .Invoke( is on the same line to confirm constructor invocation" } + ], + "must-match": [ + "ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes); var instance = ctor.Invoke(null);", + "var constructor = typeof(MyClass).GetConstructor(new[] { typeof(string) }); object obj = constructor.Invoke(new object[] { \"test\" });", + "ConstructorInfo ci = t.GetConstructor(paramTypes); ci.Invoke(parameters);" + ], + "must-not-match": [ + "ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);", + "var info = typeof(MyClass).GetConstructors();", + "public MyClass() { }", + "new MyClass();" ] }, { @@ -92,6 +114,17 @@ "confidence": "high", "_comment": "Detects Assembly.Load( - note LoadFile and LoadFrom are already in load_dll.json" } + ], + "must-match": [ + "Assembly assembly = Assembly.Load(\"MyAssembly\");", + "var asm = Assembly.Load(assemblyName);", + "Assembly.Load(new AssemblyName(\"Plugin.Core\"));" + ], + "must-not-match": [ + "Assembly assembly = Assembly.GetExecutingAssembly();", + "var asm = Assembly.GetCallingAssembly();", + "Assembly.LoadFrom(\"path/to/assembly.dll\");", + "Assembly.LoadFile(@\"C:\\assemblies\\mylib.dll\");" ] }, { @@ -115,6 +148,17 @@ "confidence": "high", "_comment": "Detects .InvokeMember( for dynamic member access" } + ], + "must-match": [ + "type.InvokeMember(\"MyMethod\", BindingFlags.InvokeMethod, null, obj, args);", + "var result = typeof(MyClass).InvokeMember(\"Execute\", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, instance, null);", + "t.InvokeMember(methodName, flags, binder, target, parameters);" + ], + "must-not-match": [ + "var members = type.GetMembers();", + "type.GetMethod(\"MyMethod\");", + "obj.MyMethod();", + "InvokeMemberAsync(name, args);" ] }, { @@ -138,6 +182,18 @@ "confidence": "high", "_comment": "Detects Activator.CreateInstance for dynamic object instantiation" } + ], + "must-match": [ + "object instance = Activator.CreateInstance(typeof(MyClass));", + "var obj = Activator.CreateInstance(\"MyAssembly\", \"MyNamespace.MyClass\");", + "T instance = (T)Activator.CreateInstance(type);", + "Activator.CreateInstance();" + ], + "must-not-match": [ + "new MyClass();", + "var instance = new MyClass();", + "MyClass.Create();", + "CreateInstance();" ] }, { @@ -161,6 +217,17 @@ "confidence": "medium", "_comment": "Detects .GetMethod( which is often used before dynamic invocation" } + ], + "must-match": [ + "MethodInfo method = type.GetMethod(\"Execute\");", + "var methodInfo = typeof(MyClass).GetMethod(\"Test\", BindingFlags.Public | BindingFlags.Instance);", + "Type t = typeof(string); t.GetMethod(\"ToUpper\");" + ], + "must-not-match": [ + "var methods = type.GetMethods();", + "public void MyMethod() { }", + "GetMethodAsync(name);", + "CustomGetMethod();" ] }, { @@ -184,6 +251,17 @@ "confidence": "medium", "_comment": "Detects .GetConstructor( which is often used before dynamic object creation" } + ], + "must-match": [ + "ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);", + "var constructor = typeof(MyClass).GetConstructor(new[] { typeof(string), typeof(int) });", + "Type t = GetType(); t.GetConstructor(parameterTypes);" + ], + "must-not-match": [ + "var constructors = type.GetConstructors();", + "public MyClass() { }", + "GetConstructorAsync();", + "CustomGetConstructor();" ] }, { @@ -207,6 +285,17 @@ "confidence": "medium", "_comment": "Detects Type.GetType( for dynamic type loading from string" } + ], + "must-match": [ + "Type type = Type.GetType(\"System.String\");", + "var t = Type.GetType(\"MyNamespace.MyClass, MyAssembly\");", + "Type.GetType(typeName, true);" + ], + "must-not-match": [ + "var type = typeof(MyClass);", + "Type t = obj.GetType();", + "GetType();", + "myObject.GetType();" ] } ] From 0677b444f0f187bec61dced1241150e76c20e1f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 19:34:02 +0000 Subject: [PATCH 6/7] Fix compilation error in ReflectionTests.cs - add missing using and fully qualify Languages type Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- AppInspector.Tests/RuleProcessor/ReflectionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AppInspector.Tests/RuleProcessor/ReflectionTests.cs b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs index b87bb4b9..db259001 100644 --- a/AppInspector.Tests/RuleProcessor/ReflectionTests.cs +++ b/AppInspector.Tests/RuleProcessor/ReflectionTests.cs @@ -2,13 +2,14 @@ using System.Linq; using System.Text; using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.CST.RecursiveExtractor; using Xunit; namespace AppInspector.Tests.RuleProcessor; public class ReflectionTests { - private readonly Languages _languages = new(); + private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); [Fact] public void DetectMethodInfoInvoke() From 12ff371ab0cdb5694f3073d589df9c8a0f95bdab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:47:04 +0000 Subject: [PATCH 7/7] Fix rule validation errors - capitalize all enum values per schema requirements Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- AppInspector/rules/default/os/reflection.json | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/AppInspector/rules/default/os/reflection.json b/AppInspector/rules/default/os/reflection.json index ce1efcbc..08bc5db6 100644 --- a/AppInspector/rules/default/os/reflection.json +++ b/AppInspector/rules/default/os/reflection.json @@ -9,15 +9,15 @@ "tags": [ "OS.Reflection.MethodInvocation" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "MethodInfo|MethodBase", - "type": "regexword", + "type": "RegexWord", "scopes": [ - "code" + "Code" ], - "confidence": "medium", + "confidence": "Medium", "_comment": "Detects MethodInfo and MethodBase usage for reflection" } ], @@ -25,9 +25,9 @@ { "pattern": { "pattern": "\\.Invoke\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ] }, "search_in": "same-line", @@ -56,15 +56,15 @@ "tags": [ "OS.Reflection.ConstructorInvocation" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "ConstructorInfo", - "type": "regexword", + "type": "RegexWord", "scopes": [ - "code" + "Code" ], - "confidence": "medium", + "confidence": "Medium", "_comment": "Detects ConstructorInfo usage for reflection" } ], @@ -72,9 +72,9 @@ { "pattern": { "pattern": "\\.Invoke\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ] }, "search_in": "same-line", @@ -103,15 +103,15 @@ "tags": [ "OS.Reflection.AssemblyLoading" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "Assembly\\.Load\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ], - "confidence": "high", + "confidence": "High", "_comment": "Detects Assembly.Load( - note LoadFile and LoadFrom are already in load_dll.json" } ], @@ -137,15 +137,15 @@ "tags": [ "OS.Reflection.InvokeMember" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "\\.InvokeMember\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ], - "confidence": "high", + "confidence": "High", "_comment": "Detects .InvokeMember( for dynamic member access" } ], @@ -171,15 +171,15 @@ "tags": [ "OS.Reflection.CreateInstance" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "Activator\\.CreateInstance", - "type": "regexword", + "type": "RegexWord", "scopes": [ - "code" + "Code" ], - "confidence": "high", + "confidence": "High", "_comment": "Detects Activator.CreateInstance for dynamic object instantiation" } ], @@ -206,15 +206,15 @@ "tags": [ "OS.Reflection.GetMethod" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "\\.GetMethod\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ], - "confidence": "medium", + "confidence": "Medium", "_comment": "Detects .GetMethod( which is often used before dynamic invocation" } ], @@ -240,15 +240,15 @@ "tags": [ "OS.Reflection.GetConstructor" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "\\.GetConstructor\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ], - "confidence": "medium", + "confidence": "Medium", "_comment": "Detects .GetConstructor( which is often used before dynamic object creation" } ], @@ -274,15 +274,15 @@ "tags": [ "OS.Reflection.GetType" ], - "severity": "moderate", + "severity": "Moderate", "patterns": [ { "pattern": "Type\\.GetType\\(", - "type": "regex", + "type": "Regex", "scopes": [ - "code" + "Code" ], - "confidence": "medium", + "confidence": "Medium", "_comment": "Detects Type.GetType( for dynamic type loading from string" } ], @@ -298,4 +298,4 @@ "myObject.GetType();" ] } -] +] \ No newline at end of file