Skip to content

Commit 3a72d77

Browse files
Reject out-of-range ports at URL-parse time
SQLAlchemy's URL parser rejects a non-integer port, but ``URL.create( port=0)`` and similar integer-but-illegal values flow through untouched. Mirror the strict validation already applied to URL query parameters: require ``1 <= port <= 65535`` and raise ``ArgumentError`` otherwise so typos fail before a pool is built rather than at first connect.
1 parent 9664586 commit 3a72d77

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

src/sqlalchemydqlite/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,13 @@ def create_connect_args(self, url: URL) -> tuple[list[Any], dict[str, Any]]:
216216
any pool is built.
217217
"""
218218
host = url.host or "localhost"
219+
if url.port is not None and not (1 <= url.port <= 65535):
220+
# SQLAlchemy's URL parser normally rejects non-integer ports
221+
# but will happily carry an integer outside the legal TCP
222+
# range if the URL was constructed via ``URL.create(port=…)``.
223+
# Catch here so typos fail at URL-parse time, matching the
224+
# validation style used for known query parameters.
225+
raise ArgumentError(f"dqlite URL port {url.port!r} is out of the valid 1..65535 range")
219226
port = url.port or 9001
220227
database = url.database or "default"
221228

tests/test_dialect_dialect_config.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,28 @@ def test_max_total_rows_unparseable_raises(self) -> None:
9898
with pytest.raises(ArgumentError, match="int"):
9999
dialect.create_connect_args(url)
100100

101+
@pytest.mark.parametrize("bad_port", [0, -1, 65536, 70000])
102+
def test_invalid_port_raises(self, bad_port: int) -> None:
103+
"""A ``URL.create(port=…)`` call that smuggles an out-of-range
104+
port past SQLAlchemy's own parser must still fail at
105+
``create_connect_args`` time, matching the policy used for URL
106+
query parameters."""
107+
from sqlalchemy.engine import URL
108+
109+
dialect = DqliteDialect()
110+
url = URL.create("dqlite", host="host", port=bad_port, database="db")
111+
with pytest.raises(ArgumentError, match="out of the valid"):
112+
dialect.create_connect_args(url)
113+
114+
@pytest.mark.parametrize("good_port", [1, 9001, 65535])
115+
def test_valid_port_accepted(self, good_port: int) -> None:
116+
from sqlalchemy.engine import URL
117+
118+
dialect = DqliteDialect()
119+
url = URL.create("dqlite", host="host", port=good_port, database="db")
120+
_, kwargs = dialect.create_connect_args(url)
121+
assert kwargs["address"] == f"host:{good_port}"
122+
101123
def test_timeout_and_max_total_rows_together(self) -> None:
102124
dialect = DqliteDialect()
103125
url = make_url("dqlite://host:19001/db?timeout=3.5&max_total_rows=250")

0 commit comments

Comments
 (0)