mirror of
https://github.com/pgsty/minio.git
synced 2026-08-08 23:33:30 +03:00
fix(notify): quote libpq connection parameters
The discrete PostgreSQL connection path concatenated raw values into a libpq keyword/value string. Whitespace, quotes, or backslashes could split a value into additional parameters or make an otherwise valid configuration fail to parse; the path also used the unsupported keyword username instead of user. Render every generated value as a single-quoted libpq parameter, escape quotes and backslashes, and use the correct user key. Keep the existing connection_string form untouched. The earlier attempt to register migrated PostgreSQL and MySQL fields is deliberately absent because those key names collide with the legacy connection-string tokenizer. Focused tests cover ordinary values, whitespace, quotes, backslashes, and parameter-shaped input. Co-authored-by: ChatGPT <noreply@openai.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -403,25 +403,41 @@ func (target *PostgreSQLTarget) initPostgreSQL() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// quoteConnParam renders a value for a libpq keyword/value connection string.
|
||||
// Parameters are separated by whitespace, so an unquoted value containing a
|
||||
// space is read as the start of the next keyword and the connection fails on a
|
||||
// string the operator never wrote. Single quotes make the value opaque;
|
||||
// backslash and the quote itself are the only characters that need escaping
|
||||
// inside them.
|
||||
//
|
||||
// Quoting unconditionally rather than only when required keeps the two cases
|
||||
// from diverging: libpq treats 'localhost' and localhost identically, so there
|
||||
// is nothing to gain by deciding per value which form to emit.
|
||||
func quoteConnParam(v string) string {
|
||||
return "'" + strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(v) + "'"
|
||||
}
|
||||
|
||||
// NewPostgreSQLTarget - creates new PostgreSQL target.
|
||||
func NewPostgreSQLTarget(id string, args PostgreSQLArgs, loggerOnce logger.LogOnce) (*PostgreSQLTarget, error) {
|
||||
params := []string{args.ConnectionString}
|
||||
if args.ConnectionString == "" {
|
||||
params = []string{}
|
||||
if !args.Host.IsEmpty() {
|
||||
params = append(params, "host="+args.Host.String())
|
||||
params = append(params, "host="+quoteConnParam(args.Host.String()))
|
||||
}
|
||||
if args.Port != "" {
|
||||
params = append(params, "port="+args.Port)
|
||||
params = append(params, "port="+quoteConnParam(args.Port))
|
||||
}
|
||||
if args.Username != "" {
|
||||
params = append(params, "username="+args.Username)
|
||||
// libpq's keyword is "user"; "username" is rejected by the server
|
||||
// as an unrecognized configuration parameter.
|
||||
params = append(params, "user="+quoteConnParam(args.Username))
|
||||
}
|
||||
if args.Password != "" {
|
||||
params = append(params, "password="+args.Password)
|
||||
params = append(params, "password="+quoteConnParam(args.Password))
|
||||
}
|
||||
if args.Database != "" {
|
||||
params = append(params, "dbname="+args.Database)
|
||||
params = append(params, "dbname="+quoteConnParam(args.Database))
|
||||
}
|
||||
}
|
||||
connStr := strings.Join(params, " ")
|
||||
|
||||
@@ -50,3 +50,25 @@ func TestPsqlTableNameValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuoteConnParam(t *testing.T) {
|
||||
testCases := []struct {
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{"localhost", "'localhost'"},
|
||||
{"5432", "'5432'"},
|
||||
// The reason this function exists: parameters are whitespace
|
||||
// separated, so an unquoted space starts a new keyword.
|
||||
{"pass word", "'pass word'"},
|
||||
{"it's", `'it\'s'`},
|
||||
{`back\slash`, `'back\\slash'`},
|
||||
{`'; host=evil.example.com; x='`, `'\'; host=evil.example.com; x=\''`},
|
||||
{"", "''"},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if got := quoteConnParam(testCase.value); got != testCase.expected {
|
||||
t.Errorf("quoteConnParam(%q) = %s, expected %s", testCase.value, got, testCase.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user