fix: limit frame and total sizes when reading docker logs (#1322)

- Add per-frame size limit (1MB) to prevent single massive log entries
- Add total log size limit (5MB) for network transfer and browser rendering
- Gracefully truncate logs that exceed limits instead of consuming unbounded memory
This commit is contained in:
henrygd
2025-10-26 11:02:18 -04:00
parent 97394e775f
commit 7cf123a99e
2 changed files with 199 additions and 0 deletions

View File

@@ -32,6 +32,12 @@ const (
maxMemoryUsage uint64 = 100 * 1024 * 1024 * 1024 * 1024
// Number of log lines to request when fetching container logs
dockerLogsTail = 200
// Maximum size of a single log frame (1MB) to prevent memory exhaustion
// A single log line larger than 1MB is likely an error or misconfiguration
maxLogFrameSize = 1024 * 1024
// Maximum total log content size (5MB) to prevent memory exhaustion
// This provides a reasonable limit for network transfer and browser rendering
maxTotalLogSize = 5 * 1024 * 1024
)
type dockerManager struct {
@@ -657,6 +663,7 @@ func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error {
const headerSize = 8
var header [headerSize]byte
buf := make([]byte, 0, dockerLogsTail*200)
totalBytesRead := 0
for {
if _, err := io.ReadFull(reader, header[:]); err != nil {
@@ -671,6 +678,19 @@ func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error {
continue
}
// Prevent memory exhaustion from excessively large frames
if frameLen > maxLogFrameSize {
return fmt.Errorf("log frame size (%d) exceeds maximum (%d)", frameLen, maxLogFrameSize)
}
// Check if reading this frame would exceed total log size limit
if totalBytesRead+int(frameLen) > maxTotalLogSize {
// Read and discard remaining data to avoid blocking
_, _ = io.Copy(io.Discard, io.LimitReader(reader, int64(frameLen)))
slog.Debug("Truncating logs: limit reached", "read", totalBytesRead, "limit", maxTotalLogSize)
return nil
}
buf = allocateBuffer(buf, int(frameLen))
if _, err := io.ReadFull(reader, buf[:frameLen]); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
@@ -682,6 +702,7 @@ func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error {
return err
}
builder.Write(buf[:frameLen])
totalBytesRead += int(frameLen)
}
}