From 66f946ee54a0017318a8fcd5d53ccee574133cc4 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 19 May 2026 12:40:11 +0200 Subject: [PATCH] fix(db): redact credentials in client-merge conflict logs CodeQL flagged go/clear-text-logging: the merge conflict logger printed raw Old/New/Kept values, which for password/auth/uuid/subId fields meant credentials landed in plain-text logs. Mask those four fields at the log site so operators still see which field collided without leaking secrets. --- database/db.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/database/db.go b/database/db.go index 0ca9e3e7..60898405 100644 --- a/database/db.go +++ b/database/db.go @@ -48,6 +48,22 @@ func Dialect() string { return db.Dialector.Name() } +var sensitiveConflictFields = map[string]struct{}{ + "uuid": {}, + "password": {}, + "auth": {}, + "subId": {}, +} + +// redactConflictValues masks values for credential-bearing merge fields so +// they never reach plain-text logs. Non-sensitive fields pass through. +func redactConflictValues(x model.ClientMergeConflict) (oldV, newV, keptV any) { + if _, sensitive := sensitiveConflictFields[x.Field]; sensitive { + return "", "", "" + } + return x.Old, x.New, x.Kept +} + const ( defaultUsername = "admin" defaultPassword = "admin" @@ -249,8 +265,9 @@ func seedClientsFromInboundJSON() error { } else { conflicts := model.MergeClientRecord(row, incoming) for _, x := range conflicts { + oldV, newV, keptV := redactConflictValues(x) log.Printf("client merge: email=%s conflict on %s old=%v new=%v kept=%v", - email, x.Field, x.Old, x.New, x.Kept) + email, x.Field, oldV, newV, keptV) } if err := tx.Save(row).Error; err != nil { return err