fix: escape special characters in postgres password (#16510)

Fixes: https://github.com/coder/coder/issues/16319

This PR modifies existing escaping logic for special characters in
Postgres password, so it does fail on edge cases like `#` or `$` when
parser recognizes as invalid port.
This commit is contained in:
Marcin Tojek
2025-02-11 09:06:42 +01:00
committed by GitHub
parent 700a453968
commit 72f62578c1
2 changed files with 18 additions and 2 deletions

View File

@ -2565,6 +2565,8 @@ func parseExternalAuthProvidersFromEnv(prefix string, environ []string) ([]coder
return providers, nil return providers, nil
} }
var reInvalidPortAfterHost = regexp.MustCompile(`invalid port ".+" after host`)
// If the user provides a postgres URL with a password that contains special // If the user provides a postgres URL with a password that contains special
// characters, the URL will be invalid. We need to escape the password so that // characters, the URL will be invalid. We need to escape the password so that
// the URL parse doesn't fail at the DB connector level. // the URL parse doesn't fail at the DB connector level.
@ -2573,7 +2575,11 @@ func escapePostgresURLUserInfo(v string) (string, error) {
// I wish I could use errors.Is here, but this error is not declared as a // I wish I could use errors.Is here, but this error is not declared as a
// variable in net/url. :( // variable in net/url. :(
if err != nil { if err != nil {
if strings.Contains(err.Error(), "net/url: invalid userinfo") { // Warning: The parser may also fail with an "invalid port" error if the password contains special
// characters. It does not detect invalid user information but instead incorrectly reports an invalid port.
//
// See: https://github.com/coder/coder/issues/16319
if strings.Contains(err.Error(), "net/url: invalid userinfo") || reInvalidPortAfterHost.MatchString(err.Error()) {
// If the URL is invalid, we assume it is because the password contains // If the URL is invalid, we assume it is because the password contains
// special characters that need to be escaped. // special characters that need to be escaped.

View File

@ -351,13 +351,23 @@ func TestEscapePostgresURLUserInfo(t *testing.T) {
output: "", output: "",
err: xerrors.New("parse postgres url: parse \"postgres://local host:5432/coder\": invalid character \" \" in host name"), err: xerrors.New("parse postgres url: parse \"postgres://local host:5432/coder\": invalid character \" \" in host name"),
}, },
{
input: "postgres://coder:co?der@localhost:5432/coder",
output: "postgres://coder:co%3Fder@localhost:5432/coder",
err: nil,
},
{
input: "postgres://coder:co#der@localhost:5432/coder",
output: "postgres://coder:co%23der@localhost:5432/coder",
err: nil,
},
} }
for _, tc := range testcases { for _, tc := range testcases {
tc := tc tc := tc
t.Run(tc.input, func(t *testing.T) { t.Run(tc.input, func(t *testing.T) {
t.Parallel() t.Parallel()
o, err := escapePostgresURLUserInfo(tc.input) o, err := escapePostgresURLUserInfo(tc.input)
require.Equal(t, tc.output, o) assert.Equal(t, tc.output, o)
if tc.err != nil { if tc.err != nil {
require.Error(t, err) require.Error(t, err)
require.EqualValues(t, tc.err.Error(), err.Error()) require.EqualValues(t, tc.err.Error(), err.Error())