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