From 13e923d251a631055ecd520eb66972c38a324be6 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 12 Feb 2026 10:59:54 -0600 Subject: [PATCH] security: zero buffer pool contents before reuse Previously, resetting slice length to 0 left previous protobuf data (device IDs, OID values, credentials) in the backing array. Now zeros the full capacity before returning to the pool. --- agent.go | 4 +++- agent_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/agent.go b/agent.go index b9c65ec..8337ab6 100644 --- a/agent.go +++ b/agent.go @@ -144,7 +144,9 @@ func runSession(ctx context.Context, baseURL, token string) error { return } encoded := base64.StdEncoding.EncodeToString(bin) - *bp = bin[:0] + full := (*bp)[:cap(*bp)] + zeroBytes(full) + *bp = full[:0] bufPool.Put(bp) payload, _ := json.Marshal(map[string]string{"binary": encoded}) sendMsg(event, payload) diff --git a/agent_test.go b/agent_test.go index 7ae7c36..93542ea 100644 --- a/agent_test.go +++ b/agent_test.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "sync" "testing" "time" @@ -352,6 +353,35 @@ func TestHandleMessageRejectsOversizedPayload(t *testing.T) { } } +func TestBufferPoolZeroesOnReturn(t *testing.T) { + pool := &sync.Pool{ + New: func() any { + b := make([]byte, 0, 64) + return &b + }, + } + + // Get a buffer, write some data, return it + bp := pool.Get().(*[]byte) + *bp = append((*bp)[:0], []byte("sensitive credentials data here")...) + full := (*bp)[:cap(*bp)] + + // Simulate the zeroing that sendBinaryResult should do + zeroBytes(full) + *bp = full[:0] + pool.Put(bp) + + // Get the buffer back and verify it's zeroed + bp2 := pool.Get().(*[]byte) + full2 := (*bp2)[:cap(*bp2)] + for i, b := range full2 { + if b != 0 { + t.Errorf("byte[%d] = %d, expected 0 — pool buffer not zeroed", i, b) + break + } + } +} + func TestZeroBytes(t *testing.T) { b := []byte{1, 2, 3, 4, 5} zeroBytes(b)