Skip to content

Commit c7430eb

Browse files
kblokclaude
andcommitted
fix: validate BiDi request headers before sending to prevent Firefox connection crash
Firefox closes the WebSocket connection when sent header values containing CR/LF, crashing the test host. Pre-validate headers in ContinueAsync and RespondAsync to throw PuppeteerException before the invalid data reaches Firefox. Also guard ContinueAsync/RespondAsync/AbortAsync against calls when page-level interception is not enabled, preventing async-void handler crashes in edge cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f9d9d3b commit c7430eb

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

lib/PuppeteerSharp/Bidi/BidiHttpRequest.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,20 @@ public override InterceptResolutionState InterceptResolutionState
124124
/// <inheritdoc />
125125
public override async Task ContinueAsync(Payload payloadOverrides = null, int? priority = null)
126126
{
127+
if (!PageLevelInterceptionEnabled)
128+
{
129+
return;
130+
}
131+
127132
VerifyInterception();
128133

129134
if (!CanBeIntercepted())
130135
{
131136
return;
132137
}
133138

139+
ValidateHeaderValues(payloadOverrides?.Headers);
140+
134141
if (priority is null)
135142
{
136143
await ContinueInternalAsync(payloadOverrides).ConfigureAwait(false);
@@ -165,13 +172,20 @@ public override async Task RespondAsync(ResponseData response, int? priority = n
165172
throw new ArgumentNullException(nameof(response));
166173
}
167174

175+
if (!PageLevelInterceptionEnabled)
176+
{
177+
return;
178+
}
179+
168180
VerifyInterception();
169181

170182
if (!CanBeIntercepted())
171183
{
172184
return;
173185
}
174186

187+
ValidateResponseHeaderValues(response.Headers);
188+
175189
if (priority is null)
176190
{
177191
await RespondInternalAsync(response).ConfigureAwait(false);
@@ -200,6 +214,11 @@ public override async Task RespondAsync(ResponseData response, int? priority = n
200214
/// <inheritdoc />
201215
public override async Task AbortAsync(RequestAbortErrorCode errorCode = RequestAbortErrorCode.Failed, int? priority = null)
202216
{
217+
if (!PageLevelInterceptionEnabled)
218+
{
219+
return;
220+
}
221+
203222
VerifyInterception();
204223

205224
if (!CanBeIntercepted())
@@ -310,6 +329,53 @@ protected override void VerifyInterception()
310329
/// <inheritdoc/>
311330
protected override bool CanBeIntercepted() => _request.IsBlocked;
312331

332+
private static void ValidateHeaderValues(Dictionary<string, string> headers)
333+
{
334+
if (headers == null)
335+
{
336+
return;
337+
}
338+
339+
foreach (var kvp in headers)
340+
{
341+
if (kvp.Value != null && kvp.Value.IndexOfAny(new[] { '\n', '\r', '\0' }) >= 0)
342+
{
343+
throw new PuppeteerException($"Invalid header value for '{kvp.Key}'");
344+
}
345+
}
346+
}
347+
348+
private static void ValidateResponseHeaderValues(Dictionary<string, object> headers)
349+
{
350+
if (headers == null)
351+
{
352+
return;
353+
}
354+
355+
foreach (var kvp in headers)
356+
{
357+
if (kvp.Value is ICollection collection)
358+
{
359+
foreach (var item in collection)
360+
{
361+
var strValue = item?.ToString();
362+
if (strValue != null && strValue.IndexOfAny(new[] { '\n', '\r', '\0' }) >= 0)
363+
{
364+
throw new PuppeteerException($"Invalid header value for '{kvp.Key}'");
365+
}
366+
}
367+
}
368+
else
369+
{
370+
var strValue = kvp.Value?.ToString();
371+
if (strValue != null && strValue.IndexOfAny(new[] { '\n', '\r', '\0' }) >= 0)
372+
{
373+
throw new PuppeteerException($"Invalid header value for '{kvp.Key}'");
374+
}
375+
}
376+
}
377+
}
378+
313379
private static List<Header> ConvertToBidiHeaders(Dictionary<string, string> headers)
314380
{
315381
if (headers == null || headers.Count == 0)

0 commit comments

Comments
 (0)