fix(inbound): 415 guard and lenient JSON parse cover chunked (no Content-Length) bodies (plan R2-06 T6)

This commit is contained in:
Joseph Doherty
2026-07-13 10:01:22 -04:00
parent fb9dc7ad14
commit 91bde0fae6
3 changed files with 100 additions and 4 deletions
@@ -179,12 +179,22 @@ public static class EndpointExtensions
httpContext.Items[AuditWriteMiddleware.AuditActorItemKey] =
identity.DisplayName;
// N5: a body is present when Content-Length says so, OR when the request is
// chunked — Transfer-Encoding present and Content-Length (necessarily) absent.
// Computed once and used by BOTH the 415 guard and the JSON parse condition so
// chunked and fixed-length bodies behave identically.
var requestHeaders = httpContext.Request.Headers;
var hasBody = httpContext.Request.ContentLength > 0
|| (httpContext.Request.ContentLength is null && requestHeaders.TransferEncoding.Count > 0);
// S9: a body sent with an explicit non-JSON Content-Type is a media-type
// mismatch, not malformed JSON — reject it with 415 rather than a misleading
// 400 "invalid JSON". A body with NO Content-Type header is still parsed
// leniently as JSON below (preserving existing callers). The sniff is
// ordinal-ignore-case so "application/JSON" etc. are treated as JSON.
if (httpContext.Request.ContentLength > 0
// ordinal-ignore-case so "application/JSON" etc. are treated as JSON. N5: the
// body signal is `hasBody`, so a chunked (no Content-Length) non-JSON body is
// also caught here instead of slipping through to a misleading 400.
if (hasBody
&& !string.IsNullOrEmpty(httpContext.Request.ContentType)
&& httpContext.Request.ContentType.Contains("json", StringComparison.OrdinalIgnoreCase) == false)
{
@@ -203,7 +213,7 @@ public static class EndpointExtensions
// `Contains("json")` silently skipped JSON deserialization for any
// capitalised value, leaving `body = null` and surfacing required
// parameters as 400 "missing" even though the caller sent a valid body.
if (httpContext.Request.ContentLength > 0
if (hasBody
|| httpContext.Request.ContentType?.Contains("json", StringComparison.OrdinalIgnoreCase) == true)
{
using var doc = await JsonDocument.ParseAsync(