From e6a3a763d6c8ac793bf6ecf0c52998c9e407c296 Mon Sep 17 00:00:00 2001 From: spicysharkco <321699413+spicysharkco@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:11:21 +0000 Subject: [PATCH 1/2] Disable interpolation when reading the connection service file ConfigParser treated "%" in service file values as interpolation syntax, so a password such as "pa%ss" raised InterpolationSyntaxError when the service was selected. libpq reads the values literally. --- asyncpg/connect_utils.py | 2 +- tests/test_connect.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/asyncpg/connect_utils.py b/asyncpg/connect_utils.py index e215391b..092723a7 100644 --- a/asyncpg/connect_utils.py +++ b/asyncpg/connect_utils.py @@ -453,7 +453,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user, server_settings = {**query, **server_settings} if connection_service_file is not None and service is not None: - pg_service = configparser.ConfigParser() + pg_service = configparser.ConfigParser(interpolation=None) pg_service.read(connection_service_file) if service in pg_service.sections(): service_params = pg_service[service] diff --git a/tests/test_connect.py b/tests/test_connect.py index 1c25d8d6..6aa34fb9 100644 --- a/tests/test_connect.py +++ b/tests/test_connect.py @@ -1286,7 +1286,7 @@ def test_connect_connection_service_file(self): host=somehost database=test_dbname user=admin -password=test_password +password=test%password target_session_attrs=primary krbsrvname=fakekrbsrvname gsslib=sspi @@ -1322,7 +1322,7 @@ def test_connect_connection_service_file(self): [('somehost', 5433)], { 'user': 'admin', - 'password': 'test_password', + 'password': 'test%password', 'database': 'test_dbname', 'target_session_attrs': 'primary', 'krbsrvname': 'fakekrbsrvname', From 6e90c5179891495f5ceb569e41cc5f1b89f3a833 Mon Sep 17 00:00:00 2001 From: spicysharkco <321699413+spicysharkco@users.noreply.github.com> Date: Tue, 20 Oct 2026 05:51:41 +0000 Subject: [PATCH 2/2] Test that service file values are read literally A new test_service_literal section puts %, %%, %(w)s, $ and ${x} in host, dbname, user and password and checks each comes back unchanged. The existing case only covered a lone % before a letter, which a parser that still collapses %% would also pass. --- tests/test_connect.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_connect.py b/tests/test_connect.py index 6aa34fb9..6af91c67 100644 --- a/tests/test_connect.py +++ b/tests/test_connect.py @@ -1290,6 +1290,12 @@ def test_connect_connection_service_file(self): target_session_attrs=primary krbsrvname=fakekrbsrvname gsslib=sspi + +[test_service_literal] +host=some%host +dbname=test%%dbname +user=ad%min +password=p%%ss%(w)s$rd${x} ''')) connection_service_file.close() os.chmod(connection_service_file.name, stat.S_IWUSR | stat.S_IRUSR) @@ -1330,6 +1336,23 @@ def test_connect_connection_service_file(self): } ) }) + # Test that values are used literally: no "%" or "$" + # interpolation, and "%%" is not collapsed + self.run_testcase({ + 'dsn': 'postgresql://?service=test_service_literal', + 'env': { + 'PGSERVICEFILE': connection_service_file.name + }, + 'result': ( + [('some%host', 5432)], + { + 'user': 'ad%min', + 'password': 'p%%ss%(w)s$rd${x}', + 'database': 'test%%dbname', + 'target_session_attrs': 'any', + } + ) + }) # Test that envvars are overridden by service file self.run_testcase({ 'dsn': 'postgresql://?service=test_service_dbname',