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>
This commit is contained in:
Kyle Carberry
2024-06-10 15:27:11 +04:00
committed by GitHub
co-authored by Spike Curtis
parent 0d65143301
commit 7c081dcd6f
2 changed files with 46 additions and 1 deletions
+1 -1
View File
@@ -348,7 +348,7 @@ func ProtoFromLog(log Log) (*proto.Log, error) {
}
return &proto.Log{
CreatedAt: timestamppb.New(log.CreatedAt),
Output: log.Output,
Output: strings.ToValidUTF8(log.Output, "❌"),
Level: proto.Log_Level(lvl),
}, nil
}
+45
View File
@@ -231,6 +231,51 @@ func TestLogSender_SkipHugeLog(t *testing.T) {
require.ErrorIs(t, err, context.Canceled)
}
func TestLogSender_InvalidUTF8(t *testing.T) {
t.Parallel()
testCtx := testutil.Context(t, testutil.WaitShort)
ctx, cancel := context.WithCancel(testCtx)
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
fDest := newFakeLogDest()
uut := NewLogSender(logger)
t0 := dbtime.Now()
ls1 := uuid.UUID{0x11}
uut.Enqueue(ls1,
Log{
CreatedAt: t0,
Output: "test log 0, src 1\xc3\x28",
Level: codersdk.LogLevelInfo,
},
Log{
CreatedAt: t0,
Output: "test log 1, src 1",
Level: codersdk.LogLevelInfo,
})
loopErr := make(chan error, 1)
go func() {
err := uut.SendLoop(ctx, fDest)
loopErr <- err
}()
req := testutil.RequireRecvCtx(ctx, t, fDest.reqs)
require.NotNil(t, req)
require.Len(t, req.Logs, 2, "it should sanitize invalid UTF-8, but still send")
// the 0xc3, 0x28 is an invalid 2-byte sequence in UTF-8. The sanitizer replaces 0xc3 with ❌, and then
// interprets 0x28 as a 1-byte sequence "("
require.Equal(t, "test log 0, src 1❌(", req.Logs[0].GetOutput())
require.Equal(t, proto.Log_INFO, req.Logs[0].GetLevel())
require.Equal(t, "test log 1, src 1", req.Logs[1].GetOutput())
require.Equal(t, proto.Log_INFO, req.Logs[1].GetLevel())
testutil.RequireSendCtx(ctx, t, fDest.resps, &proto.BatchCreateLogsResponse{})
cancel()
err := testutil.RequireRecvCtx(testCtx, t, loopErr)
require.ErrorIs(t, err, context.Canceled)
}
func TestLogSender_Batch(t *testing.T) {
t.Parallel()
testCtx := testutil.Context(t, testutil.WaitShort)