VYPR
Medium severity5.9GHSA Advisory· Published May 12, 2026· Updated May 13, 2026

CVE-2026-42348

CVE-2026-42348

Description

OpenTelemetry.OpAmp.Client is the OpAMP client for OpenTelemetry .NET. Prior to 0.2.0-alpha.1, when receiving responses from the OpAMP server over HTTP, the OpAMP client allocates an unbounded buffer to read all bytes from the server, with no upper-bound on the number of bytes consumed. This could cause memory exhaustion in the consuming application if the configured OpAMP server is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned in the response. This vulnerability is fixed in 0.2.0-alpha.1.

Affected products

1

Patches

1
bf1fad4fa298

[OpAMP] Apply response size limits for oversized responses (#4116)

8 files changed · +448 18
  • src/OpenTelemetry.OpAmp.Client/CHANGELOG.md+4 1 modified
    @@ -8,9 +8,12 @@
     * Add ability to send custom messages.
       ([#3809](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3809))
     
    -* Add support for sticky HTTP connections via the `OpAMP-Instance-UID` header
    +* Add support for sticky HTTP connections via the `OpAMP-Instance-UID` header.
       ([#3830](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3830))
     
    +* Apply response size limits for oversized OpAMP responses.
    +  ([#4116](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4116))
    +
     ## 0.1.0-alpha.4
     
     Released 2026-Jan-14
    
  • src/OpenTelemetry.OpAmp.Client/Internal/FrameDispatcher.cs+12 3 modified
    @@ -139,9 +139,18 @@ await this.transport.SendAsync(message, token)
             }
             catch (Exception ex)
             {
    -            exceptionLogger(ex);
    -
    -            this.frameBuilder.Reset(); // Reset the builder in case of failure
    +            // Exceptions are deliberately swallowed to prevent transport errors from crashing the
    +            // host application. The frame builder is reset so the next dispatch re-sends full state.
    +
    +            // OpAmpOversizedResponseException is already logged at the transport layer with
    +            // accurate semantics (the request was delivered; only the response was discarded).
    +            // Suppress the generic "Failed to send" event here to avoid a misleading duplicate.
    +            if (ex is not OpAmpOversizedResponseException)
    +            {
    +                exceptionLogger(ex);
    +            }
    +
    +            this.frameBuilder.Reset();
             }
             finally
             {
    
  • src/OpenTelemetry.OpAmp.Client/Internal/OpAmpClientEventSource.cs+48 0 modified
    @@ -14,6 +14,9 @@ internal sealed class OpAmpClientEventSource : EventSource
         // General events 1-499
         private const int EventIdInvalidWsFrame = 1;
         private const int EventIdTransportCloseFailure = 2;
    +    private const int EventIdOversizedResponseContentLength = 3;
    +    private const int EventIdOversizedResponseBody = 4;
    +    private const int EventIdHttpResponseReceived = 5;
     
         // Service events 500-999
         private const int EventIdHeartbeatServiceStart = 500;
    @@ -59,6 +62,51 @@ public void TransportCloseFailure(string exception)
             this.WriteEvent(EventIdTransportCloseFailure, exception);
         }
     
    +    [NonEvent]
    +    public void OversizedResponseContentLengthReceived(long contentLengthBytes, int limitBytes)
    +    {
    +        if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
    +        {
    +            this.OversizedResponseContentLength(contentLengthBytes, limitBytes);
    +        }
    +    }
    +
    +    [Event(EventIdOversizedResponseContentLength, Message = "OpAMP server response discarded: Content-Length ({0} bytes) exceeds the {1}-byte limit. The request was delivered but the server response was not processed.", Level = EventLevel.Warning)]
    +    public void OversizedResponseContentLength(long contentLengthBytes, int limitBytes)
    +    {
    +        this.WriteEvent(EventIdOversizedResponseContentLength, contentLengthBytes, limitBytes);
    +    }
    +
    +    [NonEvent]
    +    public void OversizedResponseBodyReceived(int minimumBytes, int limitBytes)
    +    {
    +        if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
    +        {
    +            this.OversizedResponseBody(minimumBytes, limitBytes);
    +        }
    +    }
    +
    +    [Event(EventIdOversizedResponseBody, Message = "OpAMP server response discarded: response body is at least {0} bytes, exceeding the {1}-byte limit. The request was delivered but the server response was not processed.", Level = EventLevel.Warning)]
    +    public void OversizedResponseBody(int minimumBytes, int limitBytes)
    +    {
    +        this.WriteEvent(EventIdOversizedResponseBody, minimumBytes, limitBytes);
    +    }
    +
    +    [NonEvent]
    +    public void HttpResponseBytesReceived(int bytes)
    +    {
    +        if (this.IsEnabled(EventLevel.Verbose, EventKeywords.All))
    +        {
    +            this.HttpResponseReceived(bytes);
    +        }
    +    }
    +
    +    [Event(EventIdHttpResponseReceived, Message = "OpAMP HTTP response received: {0} bytes.", Level = EventLevel.Verbose)]
    +    public void HttpResponseReceived(int bytes)
    +    {
    +        this.WriteEvent(EventIdHttpResponseReceived, bytes);
    +    }
    +
         [Event(EventIdHeartbeatServiceStart, Message = "Heartbeat service started.", Level = EventLevel.Informational)]
         public void HeartbeatServiceStart()
         {
    
  • src/OpenTelemetry.OpAmp.Client/Internal/Transport/Http/PlainHttpTransport.cs+94 9 modified
    @@ -5,6 +5,7 @@
     using System.Net.Http;
     #endif
     
    +using System.Buffers;
     using Google.Protobuf;
     using OpenTelemetry.Internal;
     using OpenTelemetry.OpAmp.Client.Internal.Utils;
    @@ -42,19 +43,30 @@ public async Task SendAsync<T>(T message, CancellationToken token)
             byteContent.Headers.Add(HeaderContentType, "application/x-protobuf");
             byteContent.Headers.Add(HeaderOpAmpInstanceUUID, this.settings.InstanceUid.ToString());
     
    -        var response = await this.httpClient
    -            .PostAsync(this.uri, byteContent, cancellationToken: token)
    +        using var request = new HttpRequestMessage(HttpMethod.Post, this.uri)
    +        {
    +            Content = byteContent,
    +        };
    +
    +        // ResponseHeadersRead prevents HttpClient from buffering the entire response body
    +        // before we can enforce the transport size limit.
    +        using var response = await this.httpClient
    +            .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)
                 .ConfigureAwait(false);
     
             response.EnsureSuccessStatusCode();
     
    -        var responseMessage = await response.Content
    -#if NET
    -            .ReadAsByteArrayAsync(token)
    -#else
    -            .ReadAsByteArrayAsync()
    -#endif
    -            .ConfigureAwait(false);
    +        // Check Content-Length before reading if the header is present.
    +        if (response.Content.Headers.ContentLength > TransportConstants.MaxMessageSize)
    +        {
    +            OpAmpClientEventSource.Log.OversizedResponseContentLengthReceived(response.Content.Headers.ContentLength.Value, TransportConstants.MaxMessageSize);
    +            throw new OpAmpOversizedResponseException(
    +                $"OpAMP server response Content-Length ({response.Content.Headers.ContentLength}) exceeds the maximum allowed size of {TransportConstants.MaxMessageSize} bytes.");
    +        }
    +
    +        // Read the response body with a size cap to prevent uncontrolled memory allocation (CWE-789).
    +        // Content-Length can be absent or spoofed, so we enforce the limit during the read as well.
    +        var responseMessage = await ReadBoundedResponseAsync(response, token).ConfigureAwait(false);
     
             this.processor.OnServerFrame(responseMessage.AsSequence());
         }
    @@ -63,4 +75,77 @@ public void Dispose()
         {
             this.httpClient?.Dispose();
         }
    +
    +    private static async Task<byte[]> ReadBoundedResponseAsync(HttpResponseMessage response, CancellationToken token)
    +    {
    +        var stream = await response.Content
    +#if NET
    +            .ReadAsStreamAsync(token)
    +#else
    +            .ReadAsStreamAsync()
    +#endif
    +            .ConfigureAwait(false);
    +
    +#if NET
    +        await using (stream.ConfigureAwait(false))
    +#else
    +        using (stream)
    +#endif
    +        {
    +            var buffer = ArrayPool<byte>.Shared.Rent(TransportConstants.MaxMessageSize);
    +            try
    +            {
    +                var totalRead = 0;
    +                while (totalRead < TransportConstants.MaxMessageSize)
    +                {
    +                    var bytesRead = await stream
    +#if NET
    +                        .ReadAsync(buffer.AsMemory(totalRead, TransportConstants.MaxMessageSize - totalRead), token)
    +#else
    +                        .ReadAsync(buffer, totalRead, TransportConstants.MaxMessageSize - totalRead, token)
    +#endif
    +                        .ConfigureAwait(false);
    +
    +                    if (bytesRead == 0)
    +                    {
    +                        // End of stream - copy the exact number of bytes read.
    +                        OpAmpClientEventSource.Log.HttpResponseBytesReceived(totalRead);
    +                        var result = new byte[totalRead];
    +                        Buffer.BlockCopy(buffer, 0, result, 0, totalRead);
    +                        return result;
    +                    }
    +
    +                    totalRead += bytesRead;
    +                }
    +
    +                // We've read exactly MaxMessageSize bytes. Check if there's more data.
    +                var probe = new byte[1];
    +                var extra = await stream
    +#if NET
    +                    .ReadAsync(probe.AsMemory(0, 1), token)
    +#else
    +                    .ReadAsync(probe, 0, 1, token)
    +#endif
    +                    .ConfigureAwait(false);
    +
    +                if (extra > 0)
    +                {
    +                    // + 1: we read exactly MaxMessageSize bytes and confirmed at least one more byte exists.
    +                    OpAmpClientEventSource.Log.OversizedResponseBodyReceived(TransportConstants.MaxMessageSize + 1, TransportConstants.MaxMessageSize);
    +                    throw new OpAmpOversizedResponseException(
    +                        $"OpAMP server response body exceeds the maximum allowed size of {TransportConstants.MaxMessageSize} bytes.");
    +                }
    +
    +                OpAmpClientEventSource.Log.HttpResponseBytesReceived(totalRead);
    +                var exactResult = new byte[totalRead];
    +                Buffer.BlockCopy(buffer, 0, exactResult, 0, totalRead);
    +                return exactResult;
    +            }
    +            finally
    +            {
    +                // Clear the rented buffer to avoid leaking sensitive data, then return it to the pool.
    +                ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
    +            }
    +        }
    +    }
     }
    
  • src/OpenTelemetry.OpAmp.Client/Internal/Transport/OpAmpOversizedResponseException.cs+26 0 added
    @@ -0,0 +1,26 @@
    +// Copyright The OpenTelemetry Authors
    +// SPDX-License-Identifier: Apache-2.0
    +
    +namespace OpenTelemetry.OpAmp.Client.Internal.Transport;
    +
    +/// <summary>
    +/// Thrown when an OpAMP server response body exceeds <see cref="TransportConstants.MaxMessageSize"/>.
    +/// Distinct from a send failure - the HTTP request was accepted by the server, but the client
    +/// deliberately discarded the oversized response to prevent uncontrolled memory allocation.
    +/// </summary>
    +internal sealed class OpAmpOversizedResponseException : InvalidOperationException
    +{
    +    public OpAmpOversizedResponseException()
    +    {
    +    }
    +
    +    public OpAmpOversizedResponseException(string message)
    +        : base(message)
    +    {
    +    }
    +
    +    public OpAmpOversizedResponseException(string message, Exception innerException)
    +        : base(message, innerException)
    +    {
    +    }
    +}
    
  • src/OpenTelemetry.OpAmp.Client/Internal/Transport/TransportConstants.cs+14 0 added
    @@ -0,0 +1,14 @@
    +// Copyright The OpenTelemetry Authors
    +// SPDX-License-Identifier: Apache-2.0
    +
    +namespace OpenTelemetry.OpAmp.Client.Internal.Transport;
    +
    +internal static class TransportConstants
    +{
    +    /// <summary>
    +    /// Maximum allowed size of a single OpAMP message received from the server (128 KB).
    +    /// Applies to both HTTP and WebSocket transports. Responses exceeding this limit
    +    /// are rejected to prevent uncontrolled memory allocation.
    +    /// </summary>
    +    public const int MaxMessageSize = 128 * 1024;
    +}
    
  • src/OpenTelemetry.OpAmp.Client/Internal/Transport/WebSocket/WsReceiver.cs+1 2 modified
    @@ -12,7 +12,6 @@ internal sealed class WsReceiver : IDisposable
     {
         private const int RentalBufferSize = 4 * 1024; // 4 KB
         private const int ReceiveBufferSize = 8 * 1024; // 8 KB
    -    private const int MaxMessageSize = 128 * 1024; // 128 KB
     
         private readonly ClientWebSocket ws;
         private readonly Thread receiveThread;
    @@ -114,7 +113,7 @@ private async Task ReceiveAsync()
                     isClosed = true;
                 }
     
    -            if (totalCount > MaxMessageSize)
    +            if (totalCount > TransportConstants.MaxMessageSize)
                 {
                     // Message too large, abort the connection.
                     await this.ws
    
  • test/OpenTelemetry.OpAmp.Client.Tests/PlainHttpTransportTests.cs+249 3 modified
    @@ -2,15 +2,19 @@
     // SPDX-License-Identifier: Apache-2.0
     
     #if NETFRAMEWORK
    +using System.Diagnostics.CodeAnalysis;
     using System.Net.Http;
     #endif
     
    +using System.IO.Compression;
     using System.Text;
     using OpenTelemetry.OpAmp.Client.Internal;
    +using OpenTelemetry.OpAmp.Client.Internal.Transport;
     using OpenTelemetry.OpAmp.Client.Internal.Transport.Http;
     using OpenTelemetry.OpAmp.Client.Settings;
     using OpenTelemetry.OpAmp.Client.Tests.Mocks;
     using OpenTelemetry.OpAmp.Client.Tests.Tools;
    +using OpenTelemetry.Tests;
     using Xunit;
     
     namespace OpenTelemetry.OpAmp.Client.Tests;
    @@ -31,7 +35,7 @@ public async Task PlainHttpTransport_SendReceiveCommunication(bool useSmallPacke
             var frameProcessor = new FrameProcessor();
             frameProcessor.Subscribe(mockListener);
     
    -        var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
     
             var mockFrame = FrameGenerator.GenerateMockAgentFrame(useSmallPackets);
     
    @@ -76,7 +80,7 @@ public async Task PlainHttpTransport_UsesConfiguredHttpClientFactory()
             var frameProcessor = new FrameProcessor();
             frameProcessor.Subscribe(mockListener);
     
    -        var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
             var mockFrame = FrameGenerator.GenerateMockAgentFrame(false);
     
             // Act
    @@ -87,6 +91,248 @@ public async Task PlainHttpTransport_UsesConfiguredHttpClientFactory()
             Assert.Contains(serverReceivedHeaders, headers => headers["X-Custom-Header"] == "CustomValue");
         }
     
    +    [Fact]
    +    public async Task PlainHttpTransport_RejectsOversizedResponse()
    +    {
    +        // Arrange - stand up a fake server that returns a response body larger than the 128 KB transport limit.
    +        // SendChunked suppresses the Content-Length header so this test exercises the body-read limit,
    +        // not the Content-Length pre-check (which has its own test below).
    +        var oversizedBody = new byte[TransportConstants.MaxMessageSize + 1];
    +        using var opAmpServer = TestHttpServer.RunServer(
    +            context =>
    +            {
    +                context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    +                context.Response.ContentType = "application/x-protobuf";
    +                context.Response.SendChunked = true;
    +                context.Response.OutputStream.Write(oversizedBody, 0, oversizedBody.Length);
    +                context.Response.Close();
    +            },
    +            out var host,
    +            out var port);
    +
    +        var settings = new OpAmpClientSettings
    +        {
    +            ServerUrl = new Uri($"http://{host}:{port}"),
    +        };
    +
    +        var frameProcessor = new FrameProcessor();
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        var mockFrame = FrameGenerator.GenerateMockAgentFrame(true);
    +
    +        // Act & Assert
    +        await Assert.ThrowsAsync<OpAmpOversizedResponseException>(
    +            () => httpTransport.SendAsync(mockFrame.Frame, CancellationToken.None));
    +    }
    +
    +    [Fact]
    +#if NETFRAMEWORK
    +    [SuppressMessage("Security", "CA5399:Enable HttpClient certificate revocation list check", Justification = "Causes PlatformNotSupportedException at runtime on net462")]
    +#endif
    +    public async Task PlainHttpTransport_RejectsOversizedCompressedResponse()
    +    {
    +        // Arrange - server sends a gzip-compressed response where the compressed payload is within
    +        // MaxMessageSize (so the Content-Length pre-check is bypassed) but the decompressed body
    +        // exceeds it. When HttpClient transparently decompresses the stream, the body-read loop
    +        // must still enforce the limit on the decompressed bytes.
    +        var largeBody = new byte[TransportConstants.MaxMessageSize + 1];
    +
    +        byte[] compressedBody;
    +        using (var ms = new MemoryStream())
    +        {
    +            using (var gzip = new GZipStream(ms, CompressionLevel.Optimal))
    +            {
    +                gzip.Write(largeBody, 0, largeBody.Length);
    +            }
    +
    +            compressedBody = ms.ToArray();
    +        }
    +
    +        // All-zeroes compress extremely well; assert the compressed size is within the limit so
    +        // the test genuinely exercises the body-read path rather than the Content-Length check.
    +        Assert.True(
    +            compressedBody.Length < TransportConstants.MaxMessageSize,
    +            $"Compressed body ({compressedBody.Length} bytes) must be smaller than MaxMessageSize ({TransportConstants.MaxMessageSize}) for this test to be meaningful.");
    +
    +        using var opAmpServer = TestHttpServer.RunServer(
    +            context =>
    +            {
    +                context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    +                context.Response.ContentType = "application/x-protobuf";
    +                context.Response.Headers["Content-Encoding"] = "gzip";
    +                context.Response.ContentLength64 = compressedBody.Length;
    +                context.Response.OutputStream.Write(compressedBody, 0, compressedBody.Length);
    +                context.Response.Close();
    +            },
    +            out var host,
    +            out var port);
    +
    +        var settings = new OpAmpClientSettings
    +        {
    +            ServerUrl = new Uri($"http://{host}:{port}"),
    +            HttpClientFactory = () =>
    +            {
    +                var handler = new HttpClientHandler
    +                {
    +                    AutomaticDecompression = System.Net.DecompressionMethods.GZip,
    +#if NET
    +                    CheckCertificateRevocationList = true,
    +#endif
    +                };
    +                return new HttpClient(handler);
    +            },
    +        };
    +
    +        var frameProcessor = new FrameProcessor();
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        var mockFrame = FrameGenerator.GenerateMockAgentFrame(true);
    +
    +        // Act & Assert - the decompressed body exceeds MaxMessageSize so the body-read
    +        // limit must fire even though the Content-Length (showing compressed size) does not.
    +        await Assert.ThrowsAsync<OpAmpOversizedResponseException>(
    +            () => httpTransport.SendAsync(mockFrame.Frame, CancellationToken.None));
    +    }
    +
    +    [Fact]
    +    public async Task PlainHttpTransport_RejectsOversizedChunkedResponseBeforeServerCompletesBody()
    +    {
    +        using var thresholdReached = new ManualResetEventSlim();
    +        using var allowServerToFinish = new ManualResetEventSlim();
    +
    +        using var opAmpServer = TestHttpServer.RunServer(
    +            context =>
    +            {
    +                try
    +                {
    +                    context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    +                    context.Response.ContentType = "application/x-protobuf";
    +                    context.Response.SendChunked = true;
    +
    +                    var chunk = new byte[4096];
    +                    var remaining = TransportConstants.MaxMessageSize + 1;
    +                    while (remaining > 0)
    +                    {
    +                        var bytesToWrite = Math.Min(chunk.Length, remaining);
    +                        context.Response.OutputStream.Write(chunk, 0, bytesToWrite);
    +                        context.Response.OutputStream.Flush();
    +                        remaining -= bytesToWrite;
    +                    }
    +
    +                    thresholdReached.Set();
    +
    +                    allowServerToFinish.Wait(TimeSpan.FromSeconds(10));
    +
    +                    context.Response.OutputStream.WriteByte(0);
    +                    context.Response.Close();
    +                }
    +                catch (System.Net.HttpListenerException)
    +                {
    +                    thresholdReached.Set();
    +                }
    +                catch (ObjectDisposedException)
    +                {
    +                    thresholdReached.Set();
    +                }
    +            },
    +            out var host,
    +            out var port);
    +
    +        var settings = new OpAmpClientSettings
    +        {
    +            ServerUrl = new Uri($"http://{host}:{port}"),
    +        };
    +
    +        var frameProcessor = new FrameProcessor();
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        var mockFrame = FrameGenerator.GenerateMockAgentFrame(true);
    +
    +        var sendTask = httpTransport.SendAsync(mockFrame.Frame, CancellationToken.None);
    +
    +        Assert.True(thresholdReached.Wait(TimeSpan.FromSeconds(5)), "The server did not send enough bytes to exceed the transport limit.");
    +
    +        try
    +        {
    +            var completedTask = await Task.WhenAny(sendTask, Task.Delay(TimeSpan.FromSeconds(2)));
    +
    +            Assert.Same(sendTask, completedTask);
    +            await Assert.ThrowsAsync<OpAmpOversizedResponseException>(async () => await sendTask);
    +        }
    +        finally
    +        {
    +            allowServerToFinish.Set();
    +        }
    +    }
    +
    +    [Fact]
    +    public async Task PlainHttpTransport_RejectsResponseWithOversizedContentLength()
    +    {
    +        // Arrange - server advertises and sends a Content-Length larger than the limit.
    +        // The Content-Length pre-check in the transport should reject this before reading the body.
    +        var oversizedBody = new byte[TransportConstants.MaxMessageSize + 1];
    +        using var opAmpServer = TestHttpServer.RunServer(
    +            context =>
    +            {
    +                context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    +                context.Response.ContentType = "application/x-protobuf";
    +                context.Response.ContentLength64 = oversizedBody.Length;
    +                context.Response.OutputStream.Write(oversizedBody, 0, oversizedBody.Length);
    +                context.Response.Close();
    +            },
    +            out var host,
    +            out var port);
    +
    +        var settings = new OpAmpClientSettings
    +        {
    +            ServerUrl = new Uri($"http://{host}:{port}"),
    +        };
    +
    +        var frameProcessor = new FrameProcessor();
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        var mockFrame = FrameGenerator.GenerateMockAgentFrame(true);
    +
    +        // Act & Assert
    +        await Assert.ThrowsAsync<OpAmpOversizedResponseException>(
    +            () => httpTransport.SendAsync(mockFrame.Frame, CancellationToken.None));
    +    }
    +
    +    [Fact]
    +    public async Task PlainHttpTransport_AcceptsResponseAtExactMaxSize()
    +    {
    +        // Arrange - response body is exactly MaxMessageSize bytes (the boundary).
    +        // The bounded read should accept this; only responses strictly exceeding the limit are rejected.
    +        var body = new byte[TransportConstants.MaxMessageSize];
    +        using var opAmpServer = TestHttpServer.RunServer(
    +            context =>
    +            {
    +                context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    +                context.Response.ContentType = "application/x-protobuf";
    +                context.Response.SendChunked = true;
    +                context.Response.OutputStream.Write(body, 0, body.Length);
    +                context.Response.Close();
    +            },
    +            out var host,
    +            out var port);
    +
    +        var settings = new OpAmpClientSettings
    +        {
    +            ServerUrl = new Uri($"http://{host}:{port}"),
    +        };
    +
    +        var frameProcessor = new FrameProcessor();
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        var mockFrame = FrameGenerator.GenerateMockAgentFrame(true);
    +
    +        // Act - the response is exactly at the limit so it should NOT be rejected as oversized.
    +        // The body (zeroed bytes) is not a valid ServerToAgent message, so protobuf parsing may
    +        // throw, but the key assertion is that OpAmpOversizedResponseException is not thrown.
    +        var ex = await Record.ExceptionAsync(
    +            () => httpTransport.SendAsync(mockFrame.Frame, CancellationToken.None));
    +
    +        // Assert
    +        Assert.False(
    +            ex is OpAmpOversizedResponseException,
    +            "A response at exactly MaxMessageSize should not be rejected as oversized.");
    +    }
    +
         [Fact]
         public async Task PlainHttpTransport_SendsInstanceUUIDHeader()
         {
    @@ -104,7 +350,7 @@ public async Task PlainHttpTransport_SendsInstanceUUIDHeader()
             var frameProcessor = new FrameProcessor();
             frameProcessor.Subscribe(mockListener);
     
    -        var httpTransport = new PlainHttpTransport(settings, frameProcessor);
    +        using var httpTransport = new PlainHttpTransport(settings, frameProcessor);
             var mockFrame = FrameGenerator.GenerateMockAgentFrame(false);
     
             // Act
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.