From a5c72ba396cd52f8eabd7257f13a5c217f5f10e0 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 27 Mar 2026 15:08:47 +0000 Subject: [PATCH] fix(coderd/agentapi): trim whitespace from workspace agent metadata values (#23709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trim leading/trailing whitespace from metadata `value` and `error` fields before storage - Trimming happens before length validation so whitespace-padded values are handled correctly - Add `TrimWhitespace` test covering spaces, tabs, newlines, and preserved inner whitespace - No backfill needed (unlogged table, stores only latest value) > 🤖 Created by a Coder Agent, reviewed by me. --- coderd/agentapi/metadata.go | 3 +++ coderd/agentapi/metadata_test.go | 36 ++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/coderd/agentapi/metadata.go b/coderd/agentapi/metadata.go index 06a91c172d..12efe362ab 100644 --- a/coderd/agentapi/metadata.go +++ b/coderd/agentapi/metadata.go @@ -3,6 +3,7 @@ package agentapi import ( "context" "fmt" + "strings" "time" "github.com/google/uuid" @@ -60,6 +61,8 @@ func (a *MetadataAPI) BatchUpdateMetadata(ctx context.Context, req *agentproto.B } ) for _, md := range req.Metadata { + md.Result.Value = strings.TrimSpace(md.Result.Value) + md.Result.Error = strings.TrimSpace(md.Result.Error) metadataError := md.Result.Error allKeysLen += len(md.Key) diff --git a/coderd/agentapi/metadata_test.go b/coderd/agentapi/metadata_test.go index 93c23e95e8..17d88ae881 100644 --- a/coderd/agentapi/metadata_test.go +++ b/coderd/agentapi/metadata_test.go @@ -57,16 +57,44 @@ func TestBatchUpdateMetadata(t *testing.T) { CollectedAt: timestamppb.New(now.Add(-3 * time.Second)), Age: 3, Value: "", - Error: "uncool value", + Error: "\t uncool error ", }, }, }, } batchSize := len(req.Metadata) - // This test sends 2 metadata entries. With batch size 2, we expect - // exactly 1 capacity flush. + // This test sends 2 metadata entries (one clean, one with + // whitespace padding). With batch size 2 we expect exactly + // 1 capacity flush. The matcher verifies that stored values + // are trimmed while clean values pass through unchanged. + expectedValues := map[string]string{ + "awesome key": "awesome value", + "uncool key": "", + } + expectedErrors := map[string]string{ + "awesome key": "", + "uncool key": "uncool error", + } store.EXPECT(). - BatchUpdateWorkspaceAgentMetadata(gomock.Any(), gomock.Any()). + BatchUpdateWorkspaceAgentMetadata( + gomock.Any(), + gomock.Cond(func(arg database.BatchUpdateWorkspaceAgentMetadataParams) bool { + if len(arg.Key) != len(expectedValues) { + return false + } + for i, key := range arg.Key { + expVal, ok := expectedValues[key] + if !ok || arg.Value[i] != expVal { + return false + } + expErr, ok := expectedErrors[key] + if !ok || arg.Error[i] != expErr { + return false + } + } + return true + }), + ). Return(nil). Times(1)