Skip to content

Commit 926a58c

Browse files
Merge pull request #31 from AikidoSec/dependency-cleanups-and-improvements
Dependency cleanups and improvements
2 parents b7288f9 + b7bb414 commit 926a58c

File tree

16 files changed

+301
-264
lines changed

16 files changed

+301
-264
lines changed

Aikido.Zen.Core/Agent.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Aikido.Zen.Core.Models.Events;
1515
using Microsoft.Extensions.Logging;
1616
using Microsoft.Extensions.Logging.Abstractions;
17-
using NetTools;
1817

1918
[assembly: InternalsVisibleTo("Aikido.Zen.Tests")]
2019
namespace Aikido.Zen.Core

Aikido.Zen.Core/Aikido.Zen.Core.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@
3535
</Target>-->
3636

3737
<ItemGroup>
38-
<PackageReference Include="IPAddressRange" Version="6.1.0" />
3938
<PackageReference Include="Lib.Harmony" Version="2.3.3" />
4039
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
4140
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
4241
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
43-
<PackageReference Include="System.Text.Json" Version="9.0.0" />
42+
<PackageReference Include="System.Text.Json" Version="8.0.1" />
4443
</ItemGroup>
4544
</Project>

Aikido.Zen.Core/Helpers/IPHelper.cs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Net;
22
using System.Linq;
3-
using NetTools;
43
using System.Collections.Generic;
54
using System;
65

@@ -28,30 +27,6 @@ public static bool IsValidIp(string ip)
2827
return IPAddress.TryParse(ip, out _);
2928
}
3029

31-
/// <summary>
32-
/// Check if an IP address is in a subnet
33-
/// Example:
34-
/// Mask: 192.168.1.0/24
35-
/// Address: 192.168.1.1
36-
/// </summary>
37-
/// <param name="address"></param>
38-
/// <param name="subnet"></param>
39-
/// <returns></returns>
40-
public static bool IsInSubnet(IPAddress address, IPAddressRange subnet)
41-
{
42-
return subnet.Contains(address);
43-
}
44-
45-
/// <summary>
46-
/// Checks if a given ip string is a subnet
47-
/// </summary>
48-
/// <param name="ip"></param>
49-
/// <returns></returns>
50-
public static bool IsSubnet(string ip)
51-
{
52-
return ip.Contains("/") && IPAddressRange.TryParse(ip, out _);
53-
}
54-
5530
/// <summary>
5631
/// Converts an IP address range to a list of CIDR strings.
5732
/// </summary>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
8+
namespace Aikido.Zen.Core.Helpers
9+
{
10+
/// <summary>
11+
/// Helper class for reflection-related operations.
12+
/// </summary>
13+
public static class ReflectionHelper
14+
{
15+
private static IDictionary<string, Type> _types;
16+
private static IDictionary<string, Assembly> _assemblies;
17+
18+
static ReflectionHelper()
19+
{
20+
_types = new Dictionary<string, Type>();
21+
_assemblies = new Dictionary<string, Assembly>();
22+
}
23+
24+
/// <summary>
25+
/// Attempts to load an assembly, get a type from the loaded assembly, and then get a method from the type.
26+
/// </summary>
27+
/// <param name="assemblyName">The name of the assembly to load.</param>
28+
/// <param name="typeName">The name of the type to get from the assembly.</param>
29+
/// <param name="methodName">The name of the method to get from the type.</param>
30+
/// <param name="parameterTypeNames">The names of the parameter types for the method.</param>
31+
/// <returns>The MethodInfo of the specified method, or null if not found.</returns>
32+
public static MethodInfo GetMethodFromAssembly(string assemblyName, string typeName, string methodName, params string[] parameterTypeNames)
33+
{
34+
// Attempt to load the assembly
35+
// Attempt to get the assembly from the cache, if not found, load it
36+
if (!_assemblies.TryGetValue(assemblyName, out var assembly))
37+
{
38+
assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName);
39+
// If the assembly is not loaded, and the assembly path exists, load it
40+
if (File.Exists($"{assemblyName}.dll") && assembly == null)
41+
{
42+
assembly = Assembly.LoadFrom($"{assemblyName}.dll");
43+
}
44+
if (assembly == null) return null;
45+
_assemblies[assemblyName] = assembly;
46+
}
47+
48+
// Attempt to get the type from the cache, if not found, get it from the loaded assembly
49+
var typeKey = $"{assemblyName}.{typeName}";
50+
if (!_types.TryGetValue(typeKey, out var type))
51+
{
52+
type = assembly.ExportedTypes.FirstOrDefault(t => t.Name == typeName || t.FullName == typeName);
53+
if (type == null) return null;
54+
_types[typeKey] = type;
55+
}
56+
57+
// Use reflection to get the method
58+
var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName && m.GetParameters().All(p => parameterTypeNames.Any(ptn => ptn == p.ParameterType.FullName)));
59+
return method;
60+
}
61+
62+
public static void ClearCache()
63+
{
64+
_types.Clear();
65+
_assemblies.Clear();
66+
}
67+
}
68+
}

Aikido.Zen.Core/Models/AgentContext.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Aikido.Zen.Core.Helpers;
22
using Aikido.Zen.Core.Models.Ip;
33
using Microsoft.AspNetCore.Http;
4-
using NetTools;
54
using System;
65
using System.Collections.Generic;
76
using System.Linq;

Aikido.Zen.Core/Models/Ip/Blocklist.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Net;
55
using System.Threading;
66
using Aikido.Zen.Core.Helpers;
7-
using NetTools;
87

98
namespace Aikido.Zen.Core.Models.Ip
109
{

Aikido.Zen.Core/Patches/HttpClientPatches.cs

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,49 @@ namespace Aikido.Zen.Core.Patches
99
{
1010
internal static class HttpClientPatches
1111
{
12+
/// <summary>
13+
/// Applies patches to HttpClient methods using Harmony and reflection.
14+
/// </summary>
15+
/// <param name="harmony">The Harmony instance used for patching.</param>
1216
public static void ApplyPatches(Harmony harmony)
1317
{
14-
var asyncMethod = AccessTools.Method(typeof(HttpClient), "SendAsync", new[] {
15-
typeof(HttpRequestMessage),
16-
typeof(HttpCompletionOption),
17-
typeof(CancellationToken)
18-
});
19-
var syncMethod = AccessTools.Method(typeof(HttpClient), "Send", new[] {
20-
typeof(HttpRequestMessage),
21-
typeof(CancellationToken)
22-
});
23-
try
24-
{
25-
if (asyncMethod != null && !asyncMethod.IsAbstract)
26-
{
27-
var patchMethod = new HarmonyMethod(typeof(HttpClientPatches).GetMethod(nameof(CaptureRequest), BindingFlags.Static | BindingFlags.NonPublic));
28-
harmony.Patch(asyncMethod, patchMethod);
29-
}
18+
// Use reflection to get the methods dynamically
19+
PatchMethod(harmony, "System.Net.Http", "HttpClient", "SendAsync", "System.Net.Http.HttpRequestMessage", "System.Net.Http.HttpCompletionOption", "System.Threading.CancellationToken");
20+
PatchMethod(harmony, "System.Net.Http", "HttpClient", "Send", "System.Net.Http.HttpRequestMessage", "System.Threading.CancellationToken");
21+
}
3022

31-
if (syncMethod != null && !syncMethod.IsAbstract)
32-
{
33-
harmony.Patch(syncMethod, new HarmonyMethod(typeof(HttpClientPatches).GetMethod(nameof(CaptureRequest), BindingFlags.Static | BindingFlags.NonPublic)));
34-
}
35-
}
36-
catch (Exception)
23+
/// <summary>
24+
/// Patches a method using Harmony by dynamically retrieving it via reflection.
25+
/// </summary>
26+
/// <param name="harmony">The Harmony instance used for patching.</param>
27+
/// <param name="assemblyName">The name of the assembly containing the type.</param>
28+
/// <param name="typeName">The name of the type containing the method.</param>
29+
/// <param name="methodName">The name of the method to patch.</param>
30+
/// <param name="parameterTypeNames">The names of the parameter types for the method.</param>
31+
private static void PatchMethod(Harmony harmony, string assemblyName, string typeName, string methodName, params string[] parameterTypeNames)
32+
{
33+
var method = ReflectionHelper.GetMethodFromAssembly(assemblyName, typeName, methodName, parameterTypeNames);
34+
if (method != null && !method.IsAbstract)
3735
{
38-
// continue
36+
var patchMethod = new HarmonyMethod(typeof(HttpClientPatches).GetMethod(nameof(CaptureRequest), BindingFlags.Static | BindingFlags.NonPublic));
37+
harmony.Patch(method, patchMethod);
3938
}
40-
4139
}
4240

43-
internal static bool CaptureRequest(
44-
HttpRequestMessage request,
45-
HttpClient __instance)
41+
/// <summary>
42+
/// Callback method executed before the original HttpClient method is executed.
43+
/// </summary>
44+
/// <param name="request">The HttpRequestMessage being sent.</param>
45+
/// <param name="__instance">The instance of HttpClient being used.</param>
46+
/// <returns>True if the original method should continue execution; otherwise, false.</returns>
47+
internal static bool CaptureRequest(HttpRequestMessage request, HttpClient __instance)
4648
{
4749
var uri = __instance.BaseAddress == null
4850
? request.RequestUri
4951
: request.RequestUri == null
5052
? __instance.BaseAddress
5153
: new Uri(__instance.BaseAddress, request.RequestUri);
52-
54+
5355
var (hostname, port) = UriHelper.ExtractHost(uri);
5456
if (hostname.EndsWith("aikido.dev"))
5557
return true;

Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.csproj

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
<PropertyGroup>
44
<TargetFrameworks>net6.0;net7.0;net8.0;</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
6-
<Nullable>disable</Nullable>
6+
<Nullable>disable</Nullable>
77
<PackageOutputPath>..\nupkgs</PackageOutputPath>
8-
<AssemblyVersion></AssemblyVersion>
9-
<FileVersion></FileVersion>
8+
<AssemblyVersion>0.0.1</AssemblyVersion>
9+
<FileVersion>0.0.1</FileVersion>
1010
<UserSecretsId>97bd5157-97c6-4825-833a-933ce895cbc0</UserSecretsId>
1111
</PropertyGroup>
1212

@@ -16,20 +16,12 @@
1616
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
1717
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="2.2.2" />
1818
<PackageReference Include="Microsoft.AspNetCore.Routing.Abstractions" Version="2.2.0" />
19-
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
20-
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
21-
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
22-
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
2319
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
2420
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
2521
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
2622
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
2723
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
2824
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
29-
<PackageReference Include="MySql.Data" Version="9.1.0" />
30-
<PackageReference Include="MySqlConnector" Version="2.4.0" />
31-
<PackageReference Include="Npgsql" Version="9.0.2" />
32-
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
3325
</ItemGroup>
3426

3527
<ItemGroup>
Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,68 @@
1-
using Aikido.Zen.Core.Helpers;
1+
using System;
2+
using System.Reflection;
23
using HarmonyLib;
3-
using Microsoft.Data.Sqlite;
4-
using MySql.Data.MySqlClient;
5-
using System.Data.Common;
6-
using Npgsql;
74
using Aikido.Zen.Core.Models;
8-
using MySqlX.XDevAPI.Relational;
9-
using System.Reflection;
5+
using Aikido.Zen.Core.Helpers;
106

117
namespace Aikido.Zen.DotNetCore.Patches
128
{
139
internal static class SqlClientPatches
1410
{
15-
// we need to patch from inside the framework, because we have to pass the context, which is constructed in a framework specific manner
1611
public static void ApplyPatches(Harmony harmony)
1712
{
18-
19-
// Generic
20-
PatchMethod(harmony, typeof(DbCommand), "ExecuteNonQueryAsync");
21-
PatchMethod(harmony, typeof(DbCommand), "ExecuteReaderAsync", typeof(System.Data.CommandBehavior));
22-
PatchMethod(harmony, typeof(DbCommand), "ExecuteScalarAsync");
13+
// Use reflection to get the types dynamically
14+
PatchMethod(harmony, "System.Data.Common", "DbCommand", "ExecuteNonQueryAsync");
15+
PatchMethod(harmony, "System.Data.Common", "DbCommand", "ExecuteReaderAsync", "System.Data.CommandBehavior");
16+
PatchMethod(harmony, "System.Data.Common", "DbCommand", "ExecuteScalarAsync");
2317

2418
// SQL Server
25-
PatchMethod(harmony, typeof(Microsoft.Data.SqlClient.SqlCommand), "ExecuteNonQuery");
26-
PatchMethod(harmony, typeof(Microsoft.Data.SqlClient.SqlCommand), "ExecuteScalar");
27-
PatchMethod(harmony, typeof(Microsoft.Data.SqlClient.SqlCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
28-
PatchMethod(harmony, typeof(System.Data.SqlClient.SqlCommand), "ExecuteNonQuery");
29-
PatchMethod(harmony, typeof(System.Data.SqlClient.SqlCommand), "ExecuteScalar");
30-
PatchMethod(harmony, typeof(System.Data.SqlClient.SqlCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
19+
PatchMethod(harmony, "Microsoft.Data.SqlClient", "SqlCommand", "ExecuteNonQuery");
20+
PatchMethod(harmony, "Microsoft.Data.SqlClient", "SqlCommand", "ExecuteScalar");
21+
PatchMethod(harmony, "Microsoft.Data.SqlClient", "SqlCommand", "ExecuteReader", "System.Data.CommandBehavior");
22+
PatchMethod(harmony, "System.Data.SqlClient", "SqlCommand", "ExecuteNonQuery");
23+
PatchMethod(harmony, "System.Data.SqlClient", "SqlCommand", "ExecuteScalar");
24+
PatchMethod(harmony, "System.Data.SqlClient", "SqlCommand", "ExecuteReader", "System.Data.CommandBehavior");
3125

3226
// SQLite
33-
PatchMethod(harmony, typeof(SqliteCommand), "ExecuteNonQuery");
34-
PatchMethod(harmony, typeof(SqliteCommand), "ExecuteScalar");
35-
PatchMethod(harmony, typeof(SqliteCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
27+
PatchMethod(harmony, "Microsoft.Data.Sqlite", "SqliteCommand", "ExecuteNonQuery");
28+
PatchMethod(harmony, "Microsoft.Data.Sqlite", "SqliteCommand", "ExecuteScalar");
29+
PatchMethod(harmony, "Microsoft.Data.Sqlite", "SqliteCommand", "ExecuteReader", "System.Data.CommandBehavior");
3630

3731
// MySql, MariaDB
38-
PatchMethod(harmony, typeof(MySqlCommand), "ExecuteNonQuery");
39-
PatchMethod(harmony, typeof(MySqlCommand), "ExecuteScalar");
40-
PatchMethod(harmony, typeof(MySqlCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
41-
PatchMethod(harmony, typeof(MySqlConnector.MySqlCommand), "ExecuteNonQuery");
42-
PatchMethod(harmony, typeof(MySqlConnector.MySqlCommand), "ExecuteScalar");
43-
PatchMethod(harmony, typeof(MySqlConnector.MySqlCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
32+
PatchMethod(harmony, "MySql.Data", "MySqlClient.MySqlCommand", "ExecuteNonQuery");
33+
PatchMethod(harmony, "MySql.Data", "MySqlClient.MySqlCommand", "ExecuteScalar");
34+
PatchMethod(harmony, "MySql.Data", "MySqlClient.MySqlCommand", "ExecuteReader", "System.Data.CommandBehavior");
35+
PatchMethod(harmony, "MySqlConnector", "MySqlCommand", "ExecuteNonQuery");
36+
PatchMethod(harmony, "MySqlConnector", "MySqlCommand", "ExecuteScalar");
37+
PatchMethod(harmony, "MySqlConnector", "MySqlCommand", "ExecuteReader", "System.Data.CommandBehavior");
4438

4539
// PostgreSQL
46-
PatchMethod(harmony, typeof(NpgsqlCommand), "ExecuteNonQuery");
47-
PatchMethod(harmony, typeof(NpgsqlCommand), "ExecuteScalar");
48-
PatchMethod(harmony, typeof(NpgsqlCommand), "ExecuteReader", typeof(System.Data.CommandBehavior));
40+
PatchMethod(harmony, "Npgsql", "NpgsqlCommand", "ExecuteNonQuery");
41+
PatchMethod(harmony, "Npgsql", "NpgsqlCommand", "ExecuteScalar");
42+
PatchMethod(harmony, "Npgsql", "NpgsqlCommand", "ExecuteReader", "System.Data.CommandBehavior");
4943

5044
// MySqlX
51-
PatchMethod(harmony, typeof(Table), "Select");
52-
PatchMethod(harmony, typeof(Table), "Insert");
53-
PatchMethod(harmony, typeof(Table), "Update");
54-
PatchMethod(harmony, typeof(Table), "Delete");
45+
PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Select");
46+
PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Insert");
47+
PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Update");
48+
PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Delete");
5549
}
5650

57-
private static void PatchMethod(Harmony harmony, Type type, string methodName, params Type[] parameters)
51+
private static void PatchMethod(Harmony harmony, string assemblyName, string typeName, string methodName, params string[] parameterTypeNames)
5852
{
59-
var method = AccessTools.Method(type, methodName, parameters);
53+
var method = ReflectionHelper.GetMethodFromAssembly(assemblyName, typeName, methodName, parameterTypeNames);
6054
if (method != null)
6155
{
6256
harmony.Patch(method, new HarmonyMethod(typeof(SqlClientPatches).GetMethod(nameof(OnCommandExecuting), BindingFlags.Static | BindingFlags.NonPublic)));
6357
}
6458
}
6559

66-
private static bool OnCommandExecuting(object[] __args, MethodBase __originalMethod, DbCommand __instance)
60+
private static bool OnCommandExecuting(object[] __args, MethodBase __originalMethod, object __instance)
6761
{
62+
var dbCommand = __instance as System.Data.Common.DbCommand;
63+
if (dbCommand == null) return true;
6864
var assembly = __instance.GetType().Assembly.FullName?.Split(", Culture=")[0];
69-
return Aikido.Zen.Core.Patches.SqlClientPatcher.OnCommandExecuting(__args, __originalMethod, __instance, assembly, Zen.GetContext());
65+
return Aikido.Zen.Core.Patches.SqlClientPatcher.OnCommandExecuting(__args, __originalMethod, dbCommand, assembly, Zen.GetContext());
7066
}
7167
}
7268
}

0 commit comments

Comments
 (0)