Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
329ca37
feat: audit MCP server config changes
ibetitsmike Aug 7, 2026
f7d4945
chore: polish CODAGT-717 comments per cleanup gate
ibetitsmike Aug 7, 2026
9682815
test(coderd): audit denied MCP server config deletions
ibetitsmike Aug 8, 2026
a72d5cf
fix(enterprise/audit): redact MCP server endpoint URLs in audit diffs
ibetitsmike Aug 8, 2026
0a32b24
fix(coderd): mark deleted MCP server configs in audit logs
ibetitsmike Aug 8, 2026
08a7219
fix(coderd): make MCP config audit records match persisted rows
ibetitsmike Aug 8, 2026
09fb6fb
fix(coderd): audit MCP config deletions against the locked row
ibetitsmike Aug 10, 2026
86d42a7
chore(coderd/database/migrations): renumber migration 000566 to 000569
ibetitsmike Aug 11, 2026
197a759
chore(coderd/database/migrations): renumber migration 000569 to 000570
ibetitsmike Aug 11, 2026
a04fd6f
fix(coderd): address MCP audit review feedback
ibetitsmike Aug 11, 2026
6316be4
fix(enterprise/audit): track MCP server endpoint URLs in audit diffs
ibetitsmike Aug 13, 2026
a848d84
fix(coderd/database): renumber MCP audit migration to 000571
ibetitsmike Aug 13, 2026
db41667
chore: shorten the audit layer reread comment per cleanup gate
ibetitsmike Aug 13, 2026
a0a84be
fix(coderd): drop whitespace-only MCP display name rejection
ibetitsmike Aug 17, 2026
7dfb53e
test(coderd): pass the organization to MCP config SDK calls
ibetitsmike Aug 17, 2026
9eed1a3
fix(coderd/database): renumber MCP audit migration to 000572
ibetitsmike Aug 17, 2026
a3af59d
fix(coderd/audit): fall back to the slug for empty MCP display names
ibetitsmike Aug 17, 2026
bb776b8
test(coderd): audit MCP config creation only when a row persists
ibetitsmike Aug 17, 2026
e39972f
fix(coderd/audit): retain identifiable MCP audit targets
ibetitsmike Aug 18, 2026
d3d9854
fix(enterprise/audit): canonicalize empty MCP fields
ibetitsmike Aug 18, 2026
f48ed8f
test(enterprise/audit): cover MCP header transitions
ibetitsmike Aug 18, 2026
146d8c8
fix(coderd): gate MCP audit links on management access
ibetitsmike Aug 18, 2026
a03f5ff
fix(coderd): match MCP audit links to settings page access
ibetitsmike Aug 18, 2026
b0ffee0
fix(coderd/database): renumber MCP audit migration to 000575
ibetitsmike Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(coderd): mark deleted MCP server configs in audit logs
  • Loading branch information
ibetitsmike committed Aug 19, 2026
commit 0a32b24f87da0ab5ce4127a8664ef698daa19eaf
12 changes: 12 additions & 0 deletions coderd/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,18 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
api.Logger.Error(ctx, "unable to fetch chat", slog.Error(err))
}
return false
case database.ResourceTypeMCPServerConfig:
Comment thread
ibetitsmike marked this conversation as resolved.
// MCP server configs are hard-deleted, so a 404 means deleted.
_, err := api.Database.GetMCPServerConfigByID(ctx, alog.AuditLog.ResourceID)
if xerrors.Is(err, sql.ErrNoRows) {
return true
}
// Config reads are org-scoped, so an auditor can lack read on
// the config's organization. That is not worth logging.
if err != nil && !dbauthz.IsNotAuthorizedError(err) {
api.Logger.Error(ctx, "unable to fetch mcp server config", slog.Error(err))
}
Comment thread
ibetitsmike marked this conversation as resolved.
return false
case database.ResourceTypeUserSecret:
_, err := api.Database.GetUserSecretByID(ctx, alog.AuditLog.ResourceID)
if xerrors.Is(err, sql.ErrNoRows) {
Expand Down
38 changes: 38 additions & 0 deletions coderd/mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,44 @@ func TestMCPServerConfigsAudit(t *testing.T) {
require.EqualValues(t, http.StatusNoContent, logs[0].StatusCode)
})

t.Run("DeletedResourceMarked", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitLong)
client, _ := newAuditedMCPClient(t)
firstUser := coderdtest.CreateFirstUser(t, client)
config := createMCPServerConfig(t, client, firstUser.OrganizationID, "audit-is-deleted", true)
deletedID := uuid.New()

err := client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
OrganizationID: firstUser.OrganizationID,
Action: codersdk.AuditActionWrite,
ResourceType: codersdk.ResourceTypeMCPServerConfig,
ResourceID: config.ID,
})
require.NoError(t, err)
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
OrganizationID: firstUser.OrganizationID,
Action: codersdk.AuditActionDelete,
ResourceType: codersdk.ResourceTypeMCPServerConfig,
ResourceID: deletedID,
})
require.NoError(t, err)

logs, err := client.AuditLogs(ctx, codersdk.AuditLogsRequest{
Pagination: codersdk.Pagination{Limit: 25},
})
require.NoError(t, err)
byResourceID := make(map[uuid.UUID]codersdk.AuditLog, len(logs.AuditLogs))
for _, alog := range logs.AuditLogs {
byResourceID[alog.ResourceID] = alog
}
require.Contains(t, byResourceID, config.ID)
require.False(t, byResourceID[config.ID].IsDeleted)
require.Contains(t, byResourceID, deletedID)
require.True(t, byResourceID[deletedID].IsDeleted)
})

t.Run("WriteDeniedAudited", func(t *testing.T) {
t.Parallel()

Expand Down