Skip to content

Commit 7c081dc

Browse files
fix: replace invalid utf-8 sequences in agent logs (#13436)
* fix: replace invalid utf-8 sequences in agent logs Fixes #13433. * fix: replace invalid UTF-8 with ❌, add regression Signed-off-by: Spike Curtis <spike@coder.com> --------- Signed-off-by: Spike Curtis <spike@coder.com> Co-authored-by: Spike Curtis <spike@coder.com>
1 parent 0d65143 commit 7c081dc

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

codersdk/agentsdk/convert.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func ProtoFromLog(log Log) (*proto.Log, error) {
348348
}
349349
return &proto.Log{
350350
CreatedAt: timestamppb.New(log.CreatedAt),
351-
Output: log.Output,
351+
Output: strings.ToValidUTF8(log.Output, "❌"),
352352
Level: proto.Log_Level(lvl),
353353
}, nil
354354
}

codersdk/agentsdk/logs_internal_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,51 @@ func TestLogSender_SkipHugeLog(t *testing.T) {
231231
require.ErrorIs(t, err, context.Canceled)
232232
}
233233

234+
func TestLogSender_InvalidUTF8(t *testing.T) {
235+
t.Parallel()
236+
testCtx := testutil.Context(t, testutil.WaitShort)
237+
ctx, cancel := context.WithCancel(testCtx)
238+
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
239+
fDest := newFakeLogDest()
240+
uut := NewLogSender(logger)
241+
242+
t0 := dbtime.Now()
243+
ls1 := uuid.UUID{0x11}
244+
245+
uut.Enqueue(ls1,
246+
Log{
247+
CreatedAt: t0,
248+
Output: "test log 0, src 1\xc3\x28",
249+
Level: codersdk.LogLevelInfo,
250+
},
251+
Log{
252+
CreatedAt: t0,
253+
Output: "test log 1, src 1",
254+
Level: codersdk.LogLevelInfo,
255+
})
256+
257+
loopErr := make(chan error, 1)
258+
go func() {
259+
err := uut.SendLoop(ctx, fDest)
260+
loopErr <- err
261+
}()
262+
263+
req := testutil.RequireRecvCtx(ctx, t, fDest.reqs)
264+
require.NotNil(t, req)
265+
require.Len(t, req.Logs, 2, "it should sanitize invalid UTF-8, but still send")
266+
// the 0xc3, 0x28 is an invalid 2-byte sequence in UTF-8. The sanitizer replaces 0xc3 with ❌, and then
267+
// interprets 0x28 as a 1-byte sequence "("
268+
require.Equal(t, "test log 0, src 1❌(", req.Logs[0].GetOutput())
269+
require.Equal(t, proto.Log_INFO, req.Logs[0].GetLevel())
270+
require.Equal(t, "test log 1, src 1", req.Logs[1].GetOutput())
271+
require.Equal(t, proto.Log_INFO, req.Logs[1].GetLevel())
272+
testutil.RequireSendCtx(ctx, t, fDest.resps, &proto.BatchCreateLogsResponse{})
273+
274+
cancel()
275+
err := testutil.RequireRecvCtx(testCtx, t, loopErr)
276+
require.ErrorIs(t, err, context.Canceled)
277+
}
278+
234279
func TestLogSender_Batch(t *testing.T) {
235280
t.Parallel()
236281
testCtx := testutil.Context(t, testutil.WaitShort)

0 commit comments

Comments
 (0)