fix(security): redact database query parameters (HH-596) (#159)

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-24 12:22:16 +08:00
committed by GitHub
co-authored by rogee
parent 57f8a64fd8
commit e8c010406d
4 changed files with 35 additions and 5 deletions
+14 -3
View File
@@ -2,6 +2,8 @@ package app
import (
"fmt"
"log"
"os"
"time"
"gorm.io/driver/postgres"
@@ -16,7 +18,7 @@ import (
// Pattern follows Chatwoot's ActiveRecord PostgreSQL setup in config/database.yml
func NewDatabase(cfg *config.DatabaseConfig, logLevel string) (*gorm.DB, error) {
dsn := cfg.DSN
// Map log level to GORM logger level
var gormLogLevel gormlogger.LogLevel
switch logLevel {
@@ -33,8 +35,8 @@ func NewDatabase(cfg *config.DatabaseConfig, logLevel string) (*gorm.DB, error)
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormlogger.Default.LogMode(gormLogLevel),
PrepareStmt: true,
Logger: newDatabaseLogger(gormLogLevel),
PrepareStmt: true,
DisableForeignKeyConstraintWhenMigrating: true, // migrate first, add FK constraints later
})
if err != nil {
@@ -58,3 +60,12 @@ func NewDatabase(cfg *config.DatabaseConfig, logLevel string) (*gorm.DB, error)
return db, nil
}
func newDatabaseLogger(level gormlogger.LogLevel) gormlogger.Interface {
return gormlogger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), gormlogger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: level,
IgnoreRecordNotFoundError: true,
ParameterizedQueries: true,
})
}
+19
View File
@@ -0,0 +1,19 @@
package app
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
)
func TestDatabaseLoggerRedactsQueryParameters(t *testing.T) {
filter, ok := newDatabaseLogger(gormlogger.Info).(gorm.ParamsFilter)
require.True(t, ok)
sql, params := filter.ParamsFilter(context.Background(), "SELECT * FROM contact_inboxes WHERE pubsub_token = ?", "secret-token")
require.Equal(t, "SELECT * FROM contact_inboxes WHERE pubsub_token = ?", sql)
require.Empty(t, params)
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (h *Handler) ServeWS(c *gin.Context) {
// Step 1: Authenticate (JWT or pubsub_token)
claims, err := h.authenticator.Authenticate(c)
if err != nil {
logger.L().Errorf("ws: authentication failed: %v", err)
logger.L().Warnf("ws: authentication rejected: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
+1 -1
View File
@@ -209,7 +209,7 @@ func (a *WSAuthenticator) AuthenticateAndServeWS(c *gin.Context) {
// Step 1: Authenticate
claims, err := a.Authenticate(c)
if err != nil {
logger.L().Errorf("ws: authentication failed: %v", err)
logger.L().Warnf("ws: authentication rejected: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}