fix: review fixes for WsReadInfo and WsUpgrade
- WsReadInfo: validate 64-bit frame payload length against maxPayload before casting to int (prevents overflow/memory exhaustion) - WsReadInfo: always send close response per RFC 6455 Section 5.5.1, including for empty close frames - WsUpgrade: restrict no-masking to leaf node connections only (browser clients must always mask frames)
This commit is contained in:
@@ -176,7 +176,10 @@ public struct WsReadInfo
|
|||||||
{
|
{
|
||||||
var (lenBuf, p2) = WsGet(stream, buf, pos, max, 8);
|
var (lenBuf, p2) = WsGet(stream, buf, pos, max, 8);
|
||||||
pos = p2;
|
pos = p2;
|
||||||
r.Remaining = (int)BinaryPrimitives.ReadUInt64BigEndian(lenBuf);
|
var len64 = BinaryPrimitives.ReadUInt64BigEndian(lenBuf);
|
||||||
|
if (len64 > (ulong)maxPayload)
|
||||||
|
throw new InvalidOperationException($"frame payload length {len64} exceeds max payload {maxPayload}");
|
||||||
|
r.Remaining = (int)len64;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,11 +267,17 @@ public struct WsReadInfo
|
|||||||
if (payload.Length > WsConstants.CloseStatusSize)
|
if (payload.Length > WsConstants.CloseStatusSize)
|
||||||
r.CloseBody = Encoding.UTF8.GetString(payload.AsSpan(WsConstants.CloseStatusSize));
|
r.CloseBody = Encoding.UTF8.GetString(payload.AsSpan(WsConstants.CloseStatusSize));
|
||||||
}
|
}
|
||||||
|
// Per RFC 6455 Section 5.5.1, always send a close response
|
||||||
if (r.CloseStatus != WsConstants.CloseStatusNoStatusReceived)
|
if (r.CloseStatus != WsConstants.CloseStatusNoStatusReceived)
|
||||||
{
|
{
|
||||||
var closeMsg = WsFrameWriter.CreateCloseMessage(r.CloseStatus, r.CloseBody ?? "");
|
var closeMsg = WsFrameWriter.CreateCloseMessage(r.CloseStatus, r.CloseBody ?? "");
|
||||||
r.PendingControlFrames.Add(new ControlFrameAction(WsConstants.CloseMessage, closeMsg));
|
r.PendingControlFrames.Add(new ControlFrameAction(WsConstants.CloseMessage, closeMsg));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Empty close frame — respond with empty close
|
||||||
|
r.PendingControlFrames.Add(new ControlFrameAction(WsConstants.CloseMessage, []));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WsConstants.PingMessage:
|
case WsConstants.PingMessage:
|
||||||
|
|||||||
@@ -62,8 +62,9 @@ public static class WsUpgrade
|
|||||||
ext.Contains(WsConstants.PmcExtension, StringComparison.OrdinalIgnoreCase);
|
ext.Contains(WsConstants.PmcExtension, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No-masking support
|
// No-masking support (leaf nodes only — browser clients must always mask)
|
||||||
bool noMasking = headers.TryGetValue(WsConstants.NoMaskingHeader, out var nmVal) &&
|
bool noMasking = kind == WsClientKind.Leaf &&
|
||||||
|
headers.TryGetValue(WsConstants.NoMaskingHeader, out var nmVal) &&
|
||||||
string.Equals(nmVal.Trim(), WsConstants.NoMaskingValue, StringComparison.OrdinalIgnoreCase);
|
string.Equals(nmVal.Trim(), WsConstants.NoMaskingValue, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Browser detection
|
// Browser detection
|
||||||
|
|||||||
Reference in New Issue
Block a user