Escape SQL username and password parameters before substituting them in to a URL. (#7089)
This commit is contained in:
parent
d810758ca2
commit
6ac5ba8945
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -54,9 +55,11 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf
|
|||
return nil, fmt.Errorf("connection_url cannot be empty")
|
||||
}
|
||||
|
||||
// QueryHelper doesn't do any SQL escaping, but if it starts to do so
|
||||
// then maybe we won't be able to use it to do URL substitution any more.
|
||||
c.ConnectionURL = dbutil.QueryHelper(c.ConnectionURL, map[string]string{
|
||||
"username": c.Username,
|
||||
"password": c.Password,
|
||||
"username": url.PathEscape(c.Username),
|
||||
"password": url.PathEscape(c.Password),
|
||||
})
|
||||
|
||||
if c.MaxOpenConnections == 0 {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package connutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSQLPasswordChars(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Username string
|
||||
Password string
|
||||
}{
|
||||
{"postgres", "password{0}"},
|
||||
{"postgres", "pass:word"},
|
||||
{"postgres", "pass/word"},
|
||||
{"postgres", "p@ssword"},
|
||||
{"postgres", "pass\"word\""},
|
||||
// Much to my surprise, CREATE USER "{{password}}" PASSWORD 'foo' worked.
|
||||
{"{{password}}", "foo"},
|
||||
{"user", "{{username}}"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Logf("username %q password %q", tc.Username, tc.Password)
|
||||
|
||||
sql := &SQLConnectionProducer{}
|
||||
ctx := context.Background()
|
||||
conf := map[string]interface{}{
|
||||
"connection_url": "postgres://{{username}}:{{password}}@localhost:5432/mydb",
|
||||
"username": tc.Username,
|
||||
"password": tc.Password,
|
||||
}
|
||||
_, err := sql.Init(ctx, conf, false)
|
||||
if err != nil {
|
||||
t.Errorf("Init error on %q %q: %+v", tc.Username, tc.Password, err)
|
||||
} else {
|
||||
// This jumps down a few layers...
|
||||
// Connection() uses sql.Open uses lib/pq uses net/url.Parse
|
||||
u, err := url.Parse(sql.ConnectionURL)
|
||||
if err != nil {
|
||||
t.Errorf("URL parse error on %q %q: %+v", tc.Username, tc.Password, err)
|
||||
} else {
|
||||
username := u.User.Username()
|
||||
password, pPresent := u.User.Password()
|
||||
if username != tc.Username {
|
||||
t.Errorf("Parsed username %q != original username %q", username, tc.Username)
|
||||
}
|
||||
if !pPresent {
|
||||
t.Errorf("Password %q not present", tc.Password)
|
||||
} else if password != tc.Password {
|
||||
t.Errorf("Parsed password %q != original password %q", password, tc.Password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue