Skip to content

Commit bf81f5c

Browse files
thibaudgermainAkramBitar
authored andcommitted
fix: hide password in log error notifier
Signed-off-by: Thibaud Germain <thibaud.germain1@ibm.com>
1 parent 05e391a commit bf81f5c

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

token/services/storage/db/sql/postgres/notifier.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"encoding/hex"
1515
"encoding/json"
1616
"fmt"
17+
"net/url"
18+
"regexp"
1719
"strconv"
1820
"strings"
1921
"sync"
@@ -128,7 +130,7 @@ func NewNotifier(
128130
Listener: &pgxlisten.Listener{
129131
Connect: func(ctx context.Context) (*pgx.Conn, error) { return pgx.Connect(ctx, dataSource) },
130132
LogError: func(ctx context.Context, err error) {
131-
logger.Errorf("error encountered in [%s]: %s", dataSource, err.Error())
133+
logger.Errorf("error encountered in [%s]: %s", redactDataSource(dataSource), err.Error())
132134
},
133135
ReconnectDelay: reconnectInterval,
134136
},
@@ -489,3 +491,21 @@ func pgChannelName(input string) string {
489491

490492
return prefix + hex.EncodeToString(sum[:])[:16] // 23 chars total
491493
}
494+
495+
// redactDataSource removes the password from a PostgreSQL connection string before logging.
496+
func redactDataSource(dataSource string) string {
497+
if strings.HasPrefix(dataSource, "postgres://") || strings.HasPrefix(dataSource, "postgresql://") {
498+
if u, err := url.Parse(dataSource); err == nil {
499+
if _, pwSet := u.User.Password(); pwSet {
500+
u.User = url.UserPassword(u.User.Username(), "xxxxx")
501+
}
502+
503+
return u.String()
504+
}
505+
}
506+
quotedKV := regexp.MustCompile(`password='[^']*'`)
507+
dataSource = quotedKV.ReplaceAllLiteralString(dataSource, "password=xxxxx")
508+
plainKV := regexp.MustCompile(`password=[^ ]*`)
509+
510+
return plainKV.ReplaceAllLiteralString(dataSource, "password=xxxxx")
511+
}

token/services/storage/db/sql/postgres/notifier_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,3 +591,65 @@ func TestNotifierGetSchema(t *testing.T) {
591591
require.Contains(t, schema, `CREATE OR REPLACE TRIGGER "trigger_public.test_table"`)
592592
require.Contains(t, schema, `AFTER INSERT ON public.test_table`)
593593
}
594+
595+
func TestRedactDataSource(t *testing.T) {
596+
tests := []struct {
597+
name string
598+
input string
599+
expected string
600+
}{
601+
// #nosec
602+
{
603+
name: "classic URL",
604+
input: "postgres://alice:s3cr3t@localhost:5432/mydb",
605+
expected: "postgres://alice:xxxxx@localhost:5432/mydb",
606+
},
607+
// #nosec
608+
{
609+
name: "postgresql scheme with password",
610+
input: "postgresql://bob:hunter2@db.example.com/prod",
611+
expected: "postgresql://bob:xxxxx@db.example.com/prod",
612+
},
613+
{
614+
name: "URL without password",
615+
input: "postgres://alice@localhost:5432/mydb",
616+
expected: "postgres://alice@localhost:5432/mydb",
617+
},
618+
{
619+
name: "URL without user info",
620+
input: "postgres://localhost:5432/mydb",
621+
expected: "postgres://localhost:5432/mydb",
622+
},
623+
{
624+
name: "DSN key=value with quoted password",
625+
input: "host=localhost user=alice password='s3cr3t' dbname=mydb",
626+
expected: "host=localhost user=alice password=xxxxx dbname=mydb",
627+
},
628+
{
629+
name: "DSN key=value with unquoted password",
630+
input: "host=localhost user=alice password=s3cr3t dbname=mydb",
631+
expected: "host=localhost user=alice password=xxxxx dbname=mydb",
632+
},
633+
{
634+
name: "DSN key=value without password",
635+
input: "host=localhost user=alice dbname=mydb",
636+
expected: "host=localhost user=alice dbname=mydb",
637+
},
638+
{
639+
name: "empty string",
640+
input: "",
641+
expected: "",
642+
},
643+
{
644+
name: "DSN with quoted password containing spaces",
645+
input: "host=localhost password='my secret pass' dbname=mydb",
646+
expected: "host=localhost password=xxxxx dbname=mydb",
647+
},
648+
}
649+
650+
for _, tc := range tests {
651+
t.Run(tc.name, func(t *testing.T) {
652+
require.Equal(t, tc.expected, redactDataSource(tc.input))
653+
})
654+
}
655+
}

0 commit comments

Comments
 (0)