Skip to content

Commit 79ca1c1

Browse files
committed
fix(bunconnect): obfuscate postgres credentials in logs
Prevent database credentials from being exposed in plain text in debug logs and String() output by masking the password in DSN strings. Closes EN-576
1 parent 21e7b9b commit 79ca1c1

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

bun/bunconnect/connect.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@ type ConnectionOptions struct {
2626

2727
func (opts ConnectionOptions) String() string {
2828
return fmt.Sprintf("dsn=%s, max-idle-conns=%d, max-open-conns=%d, conn-max-idle-time=%s, conn-max-lifetime=%s",
29-
opts.DatabaseSourceName, opts.MaxIdleConns, opts.MaxOpenConns, opts.ConnMaxIdleTime, opts.ConnMaxLifetime)
29+
obfuscateDSN(opts.DatabaseSourceName), opts.MaxIdleConns, opts.MaxOpenConns, opts.ConnMaxIdleTime, opts.ConnMaxLifetime)
30+
}
31+
32+
func obfuscateDSN(dsn string) string {
33+
u, err := url.Parse(dsn)
34+
if err != nil {
35+
return "***"
36+
}
37+
if u.User != nil {
38+
u.User = url.UserPassword(u.User.Username(), "****")
39+
}
40+
return u.String()
3041
}
3142

3243
func OpenSQLDB(ctx context.Context, options ConnectionOptions, hooks ...bun.QueryHook) (*bun.DB, error) {
@@ -35,13 +46,13 @@ func OpenSQLDB(ctx context.Context, options ConnectionOptions, hooks ...bun.Quer
3546
err error
3647
)
3748
if options.Connector == nil {
38-
logging.FromContext(ctx).Debugf("Opening database with default connector and dsn: '%s'", options.DatabaseSourceName)
49+
logging.FromContext(ctx).Debugf("Opening database with default connector and dsn: '%s'", obfuscateDSN(options.DatabaseSourceName))
3950
sqldb, err = sql.Open("pgx", options.DatabaseSourceName)
4051
if err != nil {
4152
return nil, err
4253
}
4354
} else {
44-
logging.FromContext(ctx).Debugf("Opening database with connector and dsn: '%s'", options.DatabaseSourceName)
55+
logging.FromContext(ctx).Debugf("Opening database with connector and dsn: '%s'", obfuscateDSN(options.DatabaseSourceName))
4556
connector, err := options.Connector(options.DatabaseSourceName)
4657
if err != nil {
4758
return nil, err

bun/bunconnect/connect_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package bunconnect
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestObfuscateDSN(t *testing.T) {
8+
tests := []struct {
9+
name string
10+
dsn string
11+
expected string
12+
}{
13+
{
14+
name: "with user and password",
15+
dsn: "postgres://user:secret@localhost:5432/mydb",
16+
expected: "postgres://user:%2A%2A%2A%2A@localhost:5432/mydb",
17+
},
18+
{
19+
name: "with user only",
20+
dsn: "postgres://user@localhost:5432/mydb",
21+
expected: "postgres://user:%2A%2A%2A%2A@localhost:5432/mydb",
22+
},
23+
{
24+
name: "without credentials",
25+
dsn: "postgres://localhost:5432/mydb",
26+
expected: "postgres://localhost:5432/mydb",
27+
},
28+
{
29+
name: "with query params",
30+
dsn: "postgres://user:secret@localhost:5432/mydb?sslmode=disable",
31+
expected: "postgres://user:%2A%2A%2A%2A@localhost:5432/mydb?sslmode=disable",
32+
},
33+
{
34+
name: "invalid dsn",
35+
dsn: "://invalid",
36+
expected: "***",
37+
},
38+
{
39+
name: "empty string",
40+
dsn: "",
41+
expected: "",
42+
},
43+
}
44+
45+
for _, tt := range tests {
46+
t.Run(tt.name, func(t *testing.T) {
47+
got := obfuscateDSN(tt.dsn)
48+
if got != tt.expected {
49+
t.Errorf("obfuscateDSN(%q) = %q, want %q", tt.dsn, got, tt.expected)
50+
}
51+
})
52+
}
53+
}

0 commit comments

Comments
 (0)