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 the URL parse doesn't fail at the DB connector level.
(v string)
| 3348 | // characters, the URL will be invalid. We need to escape the password so that |
| 3349 | // the URL parse doesn't fail at the DB connector level. |
| 3350 | func escapePostgresURLUserInfo(v string) (string, error) { |
| 3351 | _, err := url.Parse(v) |
| 3352 | // I wish I could use errors.Is here, but this error is not declared as a |
| 3353 | // variable in net/url. :( |
| 3354 | if err != nil { |
| 3355 | // Warning: The parser may also fail with an "invalid port" error if the password contains special |
| 3356 | // characters. It does not detect invalid user information but instead incorrectly reports an invalid port. |
| 3357 | // |
| 3358 | // See: https://github.com/coder/coder/issues/16319 |
| 3359 | if strings.Contains(err.Error(), "net/url: invalid userinfo") || reInvalidPortAfterHost.MatchString(err.Error()) { |
| 3360 | // If the URL is invalid, we assume it is because the password contains |
| 3361 | // special characters that need to be escaped. |
| 3362 | |
| 3363 | // get everything before first @ |
| 3364 | parts := strings.SplitN(v, "@", 2) |
| 3365 | if len(parts) != 2 { |
| 3366 | return "", xerrors.Errorf("invalid postgres url with userinfo: %s", v) |
| 3367 | } |
| 3368 | start := parts[0] |
| 3369 | // get password, which is the last item in start when split by : |
| 3370 | startParts := strings.Split(start, ":") |
| 3371 | password := startParts[len(startParts)-1] |
| 3372 | // escape password, and replace the last item in the startParts slice |
| 3373 | // with the escaped password. |
| 3374 | // |
| 3375 | // url.PathEscape is used here because url.QueryEscape |
| 3376 | // will not escape spaces correctly. |
| 3377 | newPassword := url.PathEscape(password) |
| 3378 | startParts[len(startParts)-1] = newPassword |
| 3379 | start = strings.Join(startParts, ":") |
| 3380 | return start + "@" + parts[1], nil |
| 3381 | } |
| 3382 | |
| 3383 | return "", xerrors.Errorf("parse postgres url: %w", err) |
| 3384 | } |
| 3385 | |
| 3386 | return v, nil |
| 3387 | } |
| 3388 | |
| 3389 | func signalNotifyContext(ctx context.Context, inv *serpent.Invocation, sig ...os.Signal) (context.Context, context.CancelFunc) { |
| 3390 | // On Windows, some of our signal functions lack support. |