Skip to content

Commit 11d06d4

Browse files
committed
fix: gate environment and json endpoints behind ShouldMapUiEndpoints in production (#117)
1 parent 9a82349 commit 11d06d4

2 files changed

Lines changed: 85 additions & 74 deletions

File tree

DebugProbe.AspNetCore.Tests/Extensions/DebugProbeProductionEndpointTests.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public async Task Production_does_not_map_ui_endpoints_by_default()
1818
var comparePageResponse = await app.Client.GetAsync($"/compare?localTraceId={app.SingleEntry.Id}");
1919
var scriptResponse = await app.Client.GetAsync("/debug/js/debugprobe-ui.js");
2020
var logoResponse = await app.Client.GetAsync("/debug/logo.png");
21+
var environmentResponse = await app.Client.GetAsync("/debug/environment");
22+
var jsonResponse = await app.Client.GetAsync($"/debug/json/{app.SingleEntry.Id}");
2123
var clearResponse = await app.Client.PostAsync("/debug/clear", null);
2224

2325
Assert.Equal(HttpStatusCode.OK, capturedResponse.StatusCode);
@@ -26,6 +28,8 @@ public async Task Production_does_not_map_ui_endpoints_by_default()
2628
Assert.Equal(HttpStatusCode.NotFound, scriptResponse.StatusCode);
2729
Assert.Equal(HttpStatusCode.NotFound, logoResponse.StatusCode);
2830
Assert.Equal(HttpStatusCode.NotFound, clearResponse.StatusCode);
31+
Assert.Equal(HttpStatusCode.NotFound, environmentResponse.StatusCode);
32+
Assert.Equal(HttpStatusCode.NotFound, jsonResponse.StatusCode);
2933
}
3034

3135
[Fact]
@@ -43,6 +47,8 @@ public async Task Production_maps_ui_endpoints_when_explicitly_allowed()
4347
var comparePageResponse = await app.Client.GetAsync($"/compare?localTraceId={app.SingleEntry.Id}");
4448
var scriptResponse = await app.Client.GetAsync("/debug/js/debugprobe-ui.js");
4549
var logoResponse = await app.Client.GetAsync("/debug/logo.png");
50+
var environmentResponse = await app.Client.GetAsync("/debug/environment");
51+
var jsonResponse = await app.Client.GetAsync($"/debug/json/{app.SingleEntry.Id}");
4652
var clearResponse = await app.Client.PostAsync("/debug/clear", null);
4753

4854
Assert.Equal(HttpStatusCode.OK, debugResponse.StatusCode);
@@ -51,10 +57,12 @@ public async Task Production_maps_ui_endpoints_when_explicitly_allowed()
5157
Assert.Equal(HttpStatusCode.OK, scriptResponse.StatusCode);
5258
Assert.Equal(HttpStatusCode.OK, logoResponse.StatusCode);
5359
Assert.Equal(HttpStatusCode.OK, clearResponse.StatusCode);
60+
Assert.Equal(HttpStatusCode.OK, environmentResponse.StatusCode);
61+
Assert.Equal(HttpStatusCode.OK, jsonResponse.StatusCode);
5462
}
5563

5664
[Fact]
57-
public async Task Production_keeps_machine_readable_debug_endpoints_available_by_default()
65+
public async Task Production_blocks_machine_readable_endpoints_by_default()
5866
{
5967
await using var app = await DebugProbeWebApplication.CreateAsync(
6068
Environments.Production,
@@ -64,8 +72,11 @@ public async Task Production_keeps_machine_readable_debug_endpoints_available_by
6472

6573
var environmentResponse = await app.Client.GetAsync("/debug/environment");
6674
var jsonResponse = await app.Client.GetAsync($"/debug/json/{app.SingleEntry.Id}");
75+
var compareResponse = await app.Client.GetAsync(
76+
$"/debug/compare/{app.SingleEntry.Id}?baseUrl=http://localhost&remoteTraceId={Guid.NewGuid()}");
6777

68-
Assert.Equal(HttpStatusCode.OK, environmentResponse.StatusCode);
69-
Assert.Equal(HttpStatusCode.OK, jsonResponse.StatusCode);
78+
Assert.Equal(HttpStatusCode.NotFound, environmentResponse.StatusCode);
79+
Assert.Equal(HttpStatusCode.NotFound, jsonResponse.StatusCode);
80+
Assert.Equal(HttpStatusCode.NotFound, compareResponse.StatusCode);
7081
}
7182
}

DebugProbe.AspNetCore/Extensions/DebugProbeExtensions.cs

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -167,100 +167,100 @@ public static IApplicationBuilder UseDebugProbe(this IApplicationBuilder app, Ac
167167
RequireDebugAuthorization(webApp.Map($"{prefix}/favicon.ico", ctx =>
168168
EmbeddedAssetWriter.WriteEmbeddedAsset(ctx, "DebugProbe.AspNetCore.Assets.images.debugprobe_favicon.ico", "image/x-icon")
169169
).ExcludeFromDescription(), options);
170-
}
171-
172-
RequireDebugAuthorization(webApp.MapGet($"{prefix}/compare/{{id}}", async (string id, string baseUrl, string remoteTraceId,
173-
DebugEntryStore store,
174-
DebugProbeOptions options) =>
175-
{
176-
var localEnvironment = store.Environment;
177-
var localEntry = store.Get(id);
178170

179-
if (localEntry is null)
171+
RequireDebugAuthorization(webApp.MapGet($"{prefix}/compare/{{id}}", async (string id, string baseUrl, string remoteTraceId,
172+
DebugEntryStore store,
173+
DebugProbeOptions options) =>
180174
{
181-
return Results.NotFound("Local trace not found");
182-
}
175+
var localEnvironment = store.Environment;
176+
var localEntry = store.Get(id);
183177

184-
if (!Guid.TryParse(remoteTraceId, out _))
185-
{
186-
return Results.BadRequest("Invalid remote trace id");
187-
}
178+
if (localEntry is null)
179+
{
180+
return Results.NotFound("Local trace not found");
181+
}
188182

189-
var validation = await CompareUrlValidator.ValidateCompareBaseUrlAsync(baseUrl, options);
183+
if (!Guid.TryParse(remoteTraceId, out _))
184+
{
185+
return Results.BadRequest("Invalid remote trace id");
186+
}
190187

191-
if (!validation.IsValid)
192-
{
193-
return Results.BadRequest(validation.Error);
194-
}
188+
var validation = await CompareUrlValidator.ValidateCompareBaseUrlAsync(baseUrl, options);
195189

196-
var remoteEnvironmentUrl = new Uri(validation.BaseUri!, $"{prefix}/environment");
190+
if (!validation.IsValid)
191+
{
192+
return Results.BadRequest(validation.Error);
193+
}
197194

198-
var remoteEntryUrl = new Uri(validation.BaseUri!, $"{prefix}/json/{remoteTraceId}");
195+
var remoteEnvironmentUrl = new Uri(validation.BaseUri!, $"{prefix}/environment");
199196

200-
DebugEntry? remoteEntry;
201-
DebugEnvironment? remoteEnvironment;
197+
var remoteEntryUrl = new Uri(validation.BaseUri!, $"{prefix}/json/{remoteTraceId}");
202198

203-
try
204-
{
205-
remoteEnvironment = await Http.GetFromJsonAsync<DebugEnvironment>(remoteEnvironmentUrl);
199+
DebugEntry? remoteEntry;
200+
DebugEnvironment? remoteEnvironment;
206201

207-
if (remoteEnvironment is null)
202+
try
208203
{
209-
return Results.BadRequest("Failed to load remote environment");
210-
}
204+
remoteEnvironment = await Http.GetFromJsonAsync<DebugEnvironment>(remoteEnvironmentUrl);
205+
206+
if (remoteEnvironment is null)
207+
{
208+
return Results.BadRequest("Failed to load remote environment");
209+
}
211210

212-
remoteEntry = await Http.GetFromJsonAsync<DebugEntry>(remoteEntryUrl);
211+
remoteEntry = await Http.GetFromJsonAsync<DebugEntry>(remoteEntryUrl);
213212

214-
if (remoteEntry is null)
213+
if (remoteEntry is null)
214+
{
215+
return Results.NotFound("Remote trace not found");
216+
}
217+
}
218+
catch
215219
{
216-
return Results.NotFound("Remote trace not found");
220+
return Results.BadRequest("Failed to reach remote server");
217221
}
218-
}
219-
catch
220-
{
221-
return Results.BadRequest("Failed to reach remote server");
222-
}
223-
224-
var diff = DebugEntryComparer.Compare(localEntry, remoteEntry);
222+
223+
var diff = DebugEntryComparer.Compare(localEntry, remoteEntry);
225224

226-
return Results.Ok(new
227-
{
228-
localTrace = localEntry,
229-
remoteTrace = remoteEntry,
230-
localEnvironment,
231-
remoteEnvironment,
232-
method = new { local = localEntry.Method, remote = remoteEntry.Method },
233-
path = new { local = localEntry.Path, remote = remoteEntry.Path },
234-
status = new { local = localEntry.StatusCode, remote = remoteEntry.StatusCode },
235-
236-
requestTime = new
225+
return Results.Ok(new
237226
{
238-
local = localEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
239-
remote = remoteEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
240-
},
241-
242-
environment = new { local = localEnvironment.Environment, remote = remoteEnvironment?.Environment ?? "" },
243-
culture = new { local = localEnvironment.Culture, remote = remoteEnvironment?.Culture ?? "" },
244-
requestBody = new { local = localEntry.RequestBody ?? "", remote = remoteEntry.RequestBody ?? "" },
245-
responseBody = new { local = localEntry.ResponseBody ?? "", remote = remoteEntry.ResponseBody ?? "" },
246-
diffs = diff
247-
});
227+
localTrace = localEntry,
228+
remoteTrace = remoteEntry,
229+
localEnvironment,
230+
remoteEnvironment,
231+
method = new { local = localEntry.Method, remote = remoteEntry.Method },
232+
path = new { local = localEntry.Path, remote = remoteEntry.Path },
233+
status = new { local = localEntry.StatusCode, remote = remoteEntry.StatusCode },
234+
235+
requestTime = new
236+
{
237+
local = localEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
238+
remote = remoteEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
239+
},
240+
241+
environment = new { local = localEnvironment.Environment, remote = remoteEnvironment?.Environment ?? "" },
242+
culture = new { local = localEnvironment.Culture, remote = remoteEnvironment?.Culture ?? "" },
243+
requestBody = new { local = localEntry.RequestBody ?? "", remote = remoteEntry.RequestBody ?? "" },
244+
responseBody = new { local = localEntry.ResponseBody ?? "", remote = remoteEntry.ResponseBody ?? "" },
245+
diffs = diff
246+
});
248247

249-
}).ExcludeFromDescription(), options);
248+
}).ExcludeFromDescription(), options);
250249

251-
RequireDebugAuthorization(webApp.MapGet($"{prefix}/environment", (DebugEntryStore store) =>
252-
{
253-
return Results.Ok(store.Environment);
250+
RequireDebugAuthorization(webApp.MapGet($"{prefix}/environment", (DebugEntryStore store) =>
251+
{
252+
return Results.Ok(store.Environment);
254253

255-
}).ExcludeFromDescription(), options);
254+
}).ExcludeFromDescription(), options);
256255

257-
RequireDebugAuthorization(webApp.MapGet($"{prefix}/json/{{id}}", (string id, DebugEntryStore store) =>
258-
{
259-
var item = store.Get(id);
256+
RequireDebugAuthorization(webApp.MapGet($"{prefix}/json/{{id}}", (string id, DebugEntryStore store) =>
257+
{
258+
var item = store.Get(id);
260259

261-
return item is null ? Results.NotFound() : Results.Json(item);
260+
return item is null ? Results.NotFound() : Results.Json(item);
262261

263-
}).ExcludeFromDescription(), options);
262+
}).ExcludeFromDescription(), options);
263+
}
264264

265265

266266
}

0 commit comments

Comments
 (0)