uptrace / bun

SQL-first Golang ORM

Home Page:https://bun.uptrace.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pgdriver.WithDSN panic when password contains a '#' and at least one letter before '#'

zhuyanxi opened this issue · comments

My PostgreSQL's password has a character '#'
(my dsn sample: postgres://postgres:1A#34@localhost:5432/postgres?sslmode=disable)

And I see pgdriver.WithDSN calls url.Parse() which will cause this error: parse "postgres://postgres:1A": invalid port ":1A" after host

The panic stack is:

panic: parse "postgres://postgres:1A": invalid port ":1A" after host

goroutine 1 [running]:
github.com/uptrace/bun/driver/pgdriver.WithDSN.func1(0xc0001ad040)
	C:/Users/yxzhu/go/pkg/mod/github.com/uptrace/bun/driver/pgdriver@v1.1.16/config.go:191 +0x1d2
github.com/uptrace/bun/driver/pgdriver.NewConnector({0xc00051dc28, 0x1, 0x1})
	C:/Users/yxzhu/go/pkg/mod/github.com/uptrace/bun/driver/pgdriver@v1.1.16/driver.go:76 +0xf1

As per documentation, the DSN should be a valid URL:

The connection URI needs to be encoded with percent-encoding if it includes symbols with special meaning in any of its parts. Here is an example where the equal sign (=) is replaced with %3D and the space character with %20:

There are many guides online for URL-encoding a string in Golang and this simple change should fix your issue:

password := "invalid#pwd"
pgdriver.WithDSN(fmt.Sprintf("postgres://postgres:%s@localhost:5432/postgres?sslmode=disable", url.QueryEscape(password)))

IMO, ensuring the validity of the URL should be the client code's responsibility.

As per documentation, the DSN should be a valid URL:

The connection URI needs to be encoded with percent-encoding if it includes symbols with special meaning in any of its parts. Here is an example where the equal sign (=) is replaced with %3D and the space character with %20:

There are many guides online for URL-encoding a string in Golang and this simple change should fix your issue:

password := "invalid#pwd"
pgdriver.WithDSN(fmt.Sprintf("postgres://postgres:%s@localhost:5432/postgres?sslmode=disable", url.QueryEscape(password)))

IMO, ensuring the validity of the URL should be the client code's responsibility.

Oh, sorry, I made a stupid mistake. I forgot to escape the dsn string.
Everything is fine now, thank you so much. Wish you a good day.

I feel like there should be safe way to check/use DSN without panic.

Maybe just export parseDSN function so users can check error on theirs end.

I feel like there should be safe way to check/use DSN without panic.
Maybe just export parseDSN function so users can check error on theirs end.

@betrok here parseDSN panics because the DSN passed by the user is not a valid URL. The underlying error is returned by url.Parse, as this example demonstrates.
There are several things one can already do to avoid the panic:

  • always URL-encode your DSN
  • in case there's a preferred way of handling it, check for the error with _, err := url.Parse(dsn) before passing the DSN along
  • pass all parameters via dedicated functional options (see an example from the docs below)
pgconn := pgdriver.NewConnector(
	pgdriver.WithNetwork("tcp"),
	pgdriver.WithAddr("localhost:5437"),
	pgdriver.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
	pgdriver.WithUser("test"),
	pgdriver.WithPassword(`^_^Sp00kyP@$$w0rd#`),  // <-- invalid URL characters here
	pgdriver.WithDatabase("test"),
)

There are more situations where parseDSN can return error leading to panic other than straight up invalid URL, including certificate verification.