From 4d45b69b033111640db3d02c2636716110f00cdc Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 21 Apr 2026 14:26:22 +0100 Subject: [PATCH] fix: stop tracking chat title in audit logs (#24564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chat titles can contain sensitive information (secrets, internal project names, etc.) and should not be visible in audit logs. - Use truncated chat UUID (first 8 chars) as `resource_target` instead of the title - Mark the `title` field as `ActionSecret` so diffs render as `••••••••`
Implementation notes Two changes: 1. `coderd/audit/request.go`: `ResourceTarget` for Chat returns `typed.ID.String()[:8]` instead of `typed.Title` 2. `enterprise/audit/table.go`: Chat `title` field tracking changed from `ActionTrack` to `ActionSecret` No frontend changes needed. The frontend already handles `secret: true` fields.
> 🤖 --- coderd/audit/request.go | 2 +- coderd/exp_chats_test.go | 18 ++++++++++-------- enterprise/audit/table.go | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/coderd/audit/request.go b/coderd/audit/request.go index c73bedcd7d..a1bc0a4be9 100644 --- a/coderd/audit/request.go +++ b/coderd/audit/request.go @@ -135,7 +135,7 @@ func ResourceTarget[T Auditable](tgt T) string { case database.AiSeatState: return "AI Seat" case database.Chat: - return typed.Title + return typed.ID.String()[:8] default: panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt)) } diff --git a/coderd/exp_chats_test.go b/coderd/exp_chats_test.go index 2693c5b43c..de3b94280c 100644 --- a/coderd/exp_chats_test.go +++ b/coderd/exp_chats_test.go @@ -272,10 +272,11 @@ func TestPostChats(t *testing.T) { } require.True(t, foundUserMessage) require.True(t, mAudit.Contains(t, database.AuditLog{ - Action: database.AuditActionCreate, - ResourceType: database.ResourceTypeChat, - ResourceID: chat.ID, - UserID: member.ID, + Action: database.AuditActionCreate, + ResourceType: database.ResourceTypeChat, + ResourceID: chat.ID, + ResourceTarget: chat.ID.String()[:8], + UserID: member.ID, })) }) @@ -4564,10 +4565,11 @@ func TestArchiveChat(t *testing.T) { require.True(t, archivedChats[0].Archived) require.True(t, mAudit.Contains(t, database.AuditLog{ - Action: database.AuditActionWrite, - ResourceType: database.ResourceTypeChat, - ResourceID: chatToArchive.ID, - UserID: firstUser.UserID, + Action: database.AuditActionWrite, + ResourceType: database.ResourceTypeChat, + ResourceID: chatToArchive.ID, + ResourceTarget: chatToArchive.ID.String()[:8], + UserID: firstUser.UserID, })) }) t.Run("NotFound", func(t *testing.T) { diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index e43bccae7a..890f3aa9e8 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -386,7 +386,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "workspace_id": ActionTrack, "build_id": ActionIgnore, // Internal lifecycle. "agent_id": ActionIgnore, // Internal lifecycle. - "title": ActionTrack, + "title": ActionSecret, "status": ActionIgnore, // Churns every message. "worker_id": ActionIgnore, // Internal. "started_at": ActionIgnore,