Skip to content

Commit 236e400

Browse files
fix: sanitize credentials from connection string parsing errors
Prevent leaking usernames and passwords in error messages when URL parsing fails. The original url.Parse error could include the full connection string with credentials. - Replace url.Parse error with generic message - Add tests using testify to verify credentials are not leaked
1 parent 1c8ea5b commit 236e400

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

msdsn/conn_str.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ func Parse(dsn string) (Config, error) {
647647
if !ok {
648648
epaString = os.Getenv("MSSQL_USE_EPA")
649649
}
650-
if epaString != "" {
650+
if epaString != "" {
651651
epaEnabled, err := strconv.ParseBool(epaString)
652652
if err != nil {
653653
return p, fmt.Errorf("invalid epa enabled value '%s': %v", epaString, err)
@@ -836,7 +836,8 @@ func splitConnectionStringURL(dsn string) (map[string]string, error) {
836836

837837
u, err := url.Parse(dsn)
838838
if err != nil {
839-
return res, err
839+
// Do not include the original error which may contain credentials
840+
return res, fmt.Errorf("unable to parse connection string: invalid URL format")
840841
}
841842

842843
if u.Scheme != "sqlserver" {

msdsn/conn_str_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,42 @@ func TestInvalidConnectionString(t *testing.T) {
5454
}
5555
}
5656

57+
func TestCredentialNotLeakedInError(t *testing.T) {
58+
// Test that when url.Parse fails, the error message does not contain credentials
59+
testCases := []struct {
60+
name string
61+
connStr string
62+
username string
63+
password string
64+
}{
65+
{
66+
name: "URL with invalid control character",
67+
connStr: "sqlserver://myuser:secretpassword@host:1433\x00invalid",
68+
username: "myuser",
69+
password: "secretpassword",
70+
},
71+
{
72+
name: "URL with password and null byte in query",
73+
connStr: "sqlserver://admin:mysecret123@server.example.com:1433?database=test\x00",
74+
username: "admin",
75+
password: "mysecret123",
76+
},
77+
}
78+
79+
for _, tc := range testCases {
80+
t.Run(tc.name, func(t *testing.T) {
81+
_, err := Parse(tc.connStr)
82+
if !assert.Error(t, err, "Expected error for invalid connection string") {
83+
return
84+
}
85+
86+
errMsg := err.Error()
87+
assert.NotContains(t, errMsg, tc.password, "Error message should not contain password")
88+
assert.NotContains(t, errMsg, tc.username, "Error message should not contain username")
89+
})
90+
}
91+
}
92+
5793
func TestValidConnectionString(t *testing.T) {
5894
type testStruct struct {
5995
connStr string

0 commit comments

Comments
 (0)