Skip to content

Do not double-decode username and password in SQLAlchemy URL#616

Open
arpitjain099 wants to merge 1 commit into
trinodb:masterfrom
arpitjain099:fix/sqlalchemy-userinfo-plus-decode
Open

Do not double-decode username and password in SQLAlchemy URL#616
arpitjain099 wants to merge 1 commit into
trinodb:masterfrom
arpitjain099:fix/sqlalchemy-userinfo-plus-decode

Conversation

@arpitjain099

Copy link
Copy Markdown
Member

Description

When connecting through SQLAlchemy, a password that contains a literal + was being corrupted into a space, which then failed authentication with a 401. The cause is a double decode: SQLAlchemy's make_url() already percent-decodes the userinfo, and create_connect_args then ran url.username and url.password through unquote_plus a second time. unquote_plus follows the form-encoding rule where + means space, but that rule only applies to the query string, not to the userinfo, so pass+word became pass word.

The fix uses url.username and url.password directly since SQLAlchemy has already decoded them. This mirrors what SQLAlchemy itself did for the same problem (see sqlalchemy/sqlalchemy#2873). The unquote_plus calls on the query parameters are intentionally left as-is, because there + really does mean space and the values are not form-decoded by SQLAlchemy.

I added a regression test with + in both the username and password, and the full tests/unit/sqlalchemy/test_dialect.py suite passes (19 tests). Closes #611.

Non-technical explanation

Passwords with a plus sign in them now work when you connect using a SQLAlchemy URL, instead of failing to log in.

Release notes

( ) This is not user-visible or docs only and no release notes are required.
(x) Release notes are required, with the following suggested text:

* Fix SQLAlchemy authentication failing for usernames or passwords that contain a `+` character. ({issue}`611`)

SQLAlchemy's make_url already percent-decodes the userinfo, so passing
url.username and url.password through unquote_plus again corrupted any
literal '+' into a space, breaking authentication for passwords that
contain a plus sign. The form-encoding '+' means space convention only
applies to the query string, not to the userinfo component, so the
existing unquote_plus calls for query parameters are left unchanged.

Fixes trinodb#611

Signed-off-by: arpitjain099 <arpitjain099@gmail.com>
@cla-bot cla-bot Bot added the cla-signed label Jul 9, 2026
Comment on lines +130 to +133
# SQLAlchemy's make_url already percent-decodes the userinfo, so we use the
# username and password as-is. Running them through unquote_plus again would
# corrupt a literal '+' into a space (form-encoding only applies to the query
# string, not to the userinfo component).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the comment actually true? form-encoding applies to whole URL AFAIK. Can you add test for the other calls to unquote_plus here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmm, also does this mean a literal %2B in the password for example would corrupt the URL? Can you test?

@hashhar hashhar requested a review from damian3031 July 10, 2026 18:38
assert cparams['auth']._password == password


def test_trino_connection_basic_auth_with_plus_in_credentials():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe parameterize the test to also test the value with literal + present?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

unquote_plus double-decodes URL credentials, corrupting passwords containing +

2 participants