Skip to content

Commit fa6278d

Browse files
authored
Merge pull request #219 from AikidoSec/fix-process-start-patching
Fix process start patching
2 parents 1a8c954 + bf38926 commit fa6278d

File tree

9 files changed

+120
-8
lines changed

9 files changed

+120
-8
lines changed

Aikido.Zen.Benchmarks/HttpHelperBenchmarks.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace Aikido.Zen.Benchmarks
1616
[HideColumns(Column.StdErr, Column.StdDev, Column.Error, Column.Min, Column.Max, Column.RatioSD)]
1717
public class HttpHelperBenchmarks
1818
{
19+
private IDictionary<string, string> _routeParams;
1920
private IDictionary<string, string> _queryParams;
2021
private IDictionary<string, string> _headers;
2122
private IDictionary<string, string> _cookies;
@@ -99,6 +100,12 @@ public void Setup()
99100
{ "param2", "value2" }
100101
};
101102

103+
_routeParams = new Dictionary<string, string>
104+
{
105+
{ "routeParam1", "value1" },
106+
{ "routeParam2", "value2" }
107+
};
108+
102109
_headers = new Dictionary<string, string>
103110
{
104111
{ "header1", "value1" },
@@ -126,6 +133,7 @@ public void Setup()
126133
public async Task ProcessJsonRequest()
127134
{
128135
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(
136+
_routeParams,
129137
_queryParams,
130138
_headers,
131139
_cookies,
@@ -140,6 +148,7 @@ public async Task ProcessJsonRequest()
140148
public async Task ProcessXmlRequest()
141149
{
142150
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(
151+
_routeParams,
143152
_queryParams,
144153
_headers,
145154
_cookies,
@@ -154,6 +163,7 @@ public async Task ProcessXmlRequest()
154163
public async Task ProcessFormRequest()
155164
{
156165
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(
166+
_routeParams,
157167
_queryParams,
158168
_headers,
159169
_cookies,
@@ -168,6 +178,7 @@ public async Task ProcessFormRequest()
168178
public async Task ProcessMultipartFormDataRequest()
169179
{
170180
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(
181+
_routeParams,
171182
_queryParams,
172183
_headers,
173184
_cookies,

Aikido.Zen.Core/Helpers/HttpHelper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class HttpDataResult
3939
/// <summary>
4040
/// Reads and flattens HTTP request data into a dictionary with dot-notation keys.
4141
/// </summary>
42+
/// <param name="routeParams">The route parameters dictionary.</param>
4243
/// <param name="queryParams">The query parameters dictionary.</param>
4344
/// <param name="headers">The headers dictionary.</param>
4445
/// <param name="cookies">The cookies dictionary.</param>
@@ -47,6 +48,7 @@ public class HttpDataResult
4748
/// <param name="contentLength">The content length of the request body.</param>
4849
/// <returns>A HttpDataResult containing both flattened data and parsed body.</returns>
4950
public static async Task<HttpDataResult> ReadAndFlattenHttpDataAsync(
51+
IDictionary<string, string> routeParams,
5052
IDictionary<string, string> queryParams,
5153
IDictionary<string, string> headers,
5254
IDictionary<string, string> cookies,
@@ -57,6 +59,9 @@ public static async Task<HttpDataResult> ReadAndFlattenHttpDataAsync(
5759
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
5860
object parsedBody = null;
5961

62+
// Process Route Parameters
63+
UserInputHelper.ProcessRouteParameters(routeParams, result);
64+
6065
// Process Query Parameters
6166
UserInputHelper.ProcessQueryParameters(queryParams, result);
6267

Aikido.Zen.Core/Helpers/ReflectionHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static MethodInfo GetMethodFromAssembly(string assemblyName, string typeN
4343
return null;
4444
}
4545

46-
// Attempt to load the assembly
46+
// Attempt to resolve the assembly
4747
if (!_assemblies.TryGetValue(assemblyName, out var assembly))
4848
{
4949
// Check if already loaded
@@ -54,7 +54,7 @@ public static MethodInfo GetMethodFromAssembly(string assemblyName, string typeN
5454
try
5555
{
5656
// Load from the shared framework if it's not already loaded
57-
// Useful for System.Diagnostics.Process which might not be loaded yet
57+
// Useful for late assemblies that might not be loaded yet (eg. `System.Diagnostics` in ProcessPatches)
5858
assembly = Assembly.Load(new AssemblyName(assemblyName));
5959
}
6060
catch

Aikido.Zen.Core/Helpers/UserInputHelper.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ public static void ProcessQueryParameters(IDictionary<string, string> queryParam
5151
}
5252
}
5353

54+
/// <summary>
55+
/// Processes route parameters and adds them to the result dictionary.
56+
/// </summary>
57+
/// <param name="routeParams">The route parameters dictionary.</param>
58+
/// <param name="result">The dictionary to store processed data.</param>
59+
public static void ProcessRouteParameters(IDictionary<string, string> routeParams, IDictionary<string, string> result)
60+
{
61+
foreach (var routeParam in routeParams)
62+
{
63+
result[$"route.{routeParam.Key}"] = routeParam.Value;
64+
}
65+
}
66+
5467
/// <summary>
5568
/// Processes headers and adds them to the result dictionary.
5669
/// </summary>

Aikido.Zen.DotNetCore/Middleware/ContextMiddleware.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Concurrent;
2+
using System.Globalization;
23
using System.Runtime.CompilerServices;
34
using Aikido.Zen.Core;
45
using Aikido.Zen.Core.Helpers;
@@ -67,6 +68,7 @@ public async Task InvokeAsync(HttpContext httpContext, RequestDelegate next)
6768
}
6869

6970
var httpData = await HttpHelper.ReadAndFlattenHttpDataAsync(
71+
routeParams: context.RouteParams,
7072
queryParams: context.Query,
7173
headers: headersDictionary.ToDictionary(h => h.Key, h => string.Join(',', h.Value)),
7274
cookies: context.Cookies,
@@ -132,6 +134,7 @@ private bool TryPrepareContext(HttpContext httpContext, out ConcurrentDictionary
132134
UserAgent = httpContext.Request.Headers.TryGetValue("User-Agent", out var userAgent) ? userAgent.FirstOrDefault() ?? string.Empty : string.Empty,
133135
Source = Environment.Version.Major >= 5 ? "DotNetCore" : "DotNetFramework",
134136
Route = GetParametrizedRoute(httpContext),
137+
RouteParams = FlattenRouteParameters(httpContext.GetRouteData()?.Values),
135138
User = httpContext.Items["Aikido.Zen.CurrentUser"] as User
136139
};
137140
return true;
@@ -147,6 +150,29 @@ private bool TryPrepareContext(HttpContext httpContext, out ConcurrentDictionary
147150

148151
}
149152

153+
private static IDictionary<string, string> FlattenRouteParameters(RouteValueDictionary routeValues)
154+
{
155+
var result = new Dictionary<string, string>();
156+
157+
foreach (var kvp in routeValues)
158+
{
159+
if (kvp.Value == null)
160+
{
161+
continue;
162+
}
163+
164+
var value = Convert.ToString(kvp.Value, CultureInfo.InvariantCulture);
165+
if (value == null)
166+
{
167+
continue;
168+
}
169+
170+
result[kvp.Key] = value;
171+
}
172+
173+
return result;
174+
}
175+
150176
/// <summary>
151177
/// Flattens query parameters into individual dictionary entries with indexing for multiple values.
152178
/// </summary>

Aikido.Zen.DotNetFramework/HttpModules/ContextModule.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
45
using System.Runtime.CompilerServices;
56
using System.Threading.Tasks;
@@ -83,13 +84,15 @@ private async Task Context_BeginRequest(object sender, EventArgs e)
8384
UserAgent = httpContext.Request.UserAgent,
8485
Source = "DotNetFramework",
8586
Route = GetParametrizedRoute(httpContext),
87+
RouteParams = FlattenRouteParameters(httpContext.Request?.RequestContext?.RouteData?.Values),
8688
};
8789

8890
Agent.Instance.SetContextMiddlewareInstalled(true);
8991

9092
var request = httpContext.Request;
9193

9294
var httpData = await HttpHelper.ReadAndFlattenHttpDataAsync(
95+
routeParams: context.RouteParams,
9396
queryParams: context.Query,
9497
headers: request.Headers.AllKeys.ToDictionary(k => k, k => request.Headers.Get(k)),
9598
cookies: request.Cookies.AllKeys.ToDictionary(k => k, k => request.Cookies[k].Value),
@@ -219,6 +222,34 @@ private static IDictionary<string, string> FlattenHeaders(System.Collections.Spe
219222
return result;
220223
}
221224

225+
private static IDictionary<string, string> FlattenRouteParameters(RouteValueDictionary routeValues)
226+
{
227+
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
228+
229+
if (routeValues == null)
230+
{
231+
return result;
232+
}
233+
234+
foreach (var kvp in routeValues)
235+
{
236+
if (kvp.Value == null)
237+
{
238+
continue;
239+
}
240+
241+
var value = Convert.ToString(kvp.Value, CultureInfo.InvariantCulture);
242+
if (value == null)
243+
{
244+
continue;
245+
}
246+
247+
result[kvp.Key] = value;
248+
}
249+
250+
return result;
251+
}
252+
222253
/// <summary>
223254
/// Gets a parameterized route from the HTTP context, matching against the route collection
224255
/// and applying route parameter detection when needed.

Aikido.Zen.Test/ApiInfoHelperTests.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public class ApiInfoHelperTests
1515
private Context CreateTestContext(
1616
Dictionary<string, string>? headers = null,
1717
object? body = null,
18-
Dictionary<string, string>? query = null)
18+
Dictionary<string, string>? query = null,
19+
Dictionary<string, string>? route = null)
1920
{
2021
string? contentType = headers?.GetValueOrDefault("content-type") ?? "application/json";
2122
Stream? bodyStream = null;
@@ -70,7 +71,9 @@ private Context CreateTestContext(
7071

7172
var queryParams = query ?? new Dictionary<string, string>();
7273
var headerDict = headers ?? new Dictionary<string, string>();
73-
var parsed = HttpHelper.ReadAndFlattenHttpDataAsync(queryParams, headerDict, new Dictionary<string, string>(), bodyStream, contentType, bodyStream?.Length ?? 0).Result;
74+
var routeParams = route ?? new Dictionary<string, string>();
75+
76+
var parsed = HttpHelper.ReadAndFlattenHttpDataAsync(routeParams, queryParams, headerDict, new Dictionary<string, string>(), bodyStream, contentType, bodyStream?.Length ?? 0).Result;
7477

7578
return new Context
7679
{
@@ -81,7 +84,7 @@ private Context CreateTestContext(
8184
Query = query ?? new Dictionary<string, string>(),
8285
RemoteAddress = "127.0.0.1",
8386
Url = "http://localhost/test",
84-
RouteParams = new Dictionary<string, string>(),
87+
RouteParams = routeParams,
8588
Cookies = new Dictionary<string, string>(),
8689
Source = "test",
8790
ParsedBody = parsed.ParsedBody,

Aikido.Zen.Test/HttpHelperTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class HttpHelperTests
1010
{
1111
[TestCaseSource(nameof(GetTestData))]
1212
public async Task ReadAndFlattenHttpDataAsync_ShouldProcessData(
13+
IDictionary<string, string> routeParams,
1314
IDictionary<string, string> queryParams,
1415
IDictionary<string, string> headers,
1516
IDictionary<string, string> cookies,
@@ -22,7 +23,7 @@ public async Task ReadAndFlattenHttpDataAsync_ShouldProcessData(
2223
using var bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(body));
2324

2425
// Act
25-
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(queryParams, headers, cookies, bodyStream, contentType, bodyStream.Length);
26+
var result = await HttpHelper.ReadAndFlattenHttpDataAsync(routeParams, queryParams, headers, cookies, bodyStream, contentType, bodyStream.Length);
2627

2728
// Assert
2829
Assert.That(result.FlattenedData, Is.Not.Null);
@@ -79,6 +80,7 @@ public static IEnumerable<TestCaseData> GetTestData()
7980
foreach (var testCase in testCases)
8081
{
8182
yield return new TestCaseData(
83+
testCase.RouteParams,
8284
testCase.QueryParams,
8385
testCase.Headers,
8486
testCase.Cookies,
@@ -92,6 +94,7 @@ public static IEnumerable<TestCaseData> GetTestData()
9294

9395
private class TestCase
9496
{
97+
public IDictionary<string, string> RouteParams { get; set; }
9598
public IDictionary<string, string> QueryParams { get; set; }
9699
public IDictionary<string, string> Headers { get; set; }
97100
public IDictionary<string, string> Cookies { get; set; }

0 commit comments

Comments
 (0)