mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix(proxy-extras): accept unencoded DB passwords in the v2 migration lock and redact its errors (#46592)
Partial backport of #46592 to stable/1.103.x: only the libpq URL userinfo encoding the index build connects with.
Cherry-picked hunk from 1ac9cb2a1d (main).
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
ee94046d86
commit
cf6ab076eb
2 changed files with 82 additions and 3 deletions
|
|
@ -708,16 +708,23 @@ class ProxyExtrasDBManager:
|
|||
(connection_limit, pool_timeout, schema, pgbouncer, sslaccept, ...) and
|
||||
translate Prisma's TLS params back, since libpq reads ``sslcert`` as a
|
||||
client certificate where Prisma reads it as the CA."""
|
||||
from urllib.parse import parse_qsl, quote, urlencode, urlparse, urlunparse
|
||||
from urllib.parse import parse_qsl, quote, unquote, urlencode, urlparse, urlunparse
|
||||
|
||||
parsed: Final = urlparse(url)
|
||||
netloc: Final = parsed.netloc
|
||||
userinfo, separator, hostport = netloc.rpartition("@")
|
||||
user, password_separator, password = userinfo.partition(":")
|
||||
canonical_userinfo: Final = quote(unquote(user), safe="") + (
|
||||
password_separator + quote(unquote(password), safe="") if password_separator else ""
|
||||
)
|
||||
canonical_netloc: Final = f"{canonical_userinfo}{separator}{hostport}" if separator else netloc
|
||||
if not parsed.query:
|
||||
return url
|
||||
return url if canonical_netloc == netloc else urlunparse(parsed._replace(netloc=canonical_netloc))
|
||||
pairs: Final = tuple(parse_qsl(parsed.query))
|
||||
kept: Final = tuple((k, v) for k, v in pairs if k in LIBPQ_URL_PARAMS)
|
||||
sslaccept: Final = next((v for k, v in pairs if k == "sslaccept"), None)
|
||||
libpq_pairs: Final = ProxyExtrasDBManager._libpq_tls_params(kept, sslaccept)
|
||||
return urlunparse(parsed._replace(query=urlencode(libpq_pairs, quote_via=quote)))
|
||||
return urlunparse(parsed._replace(netloc=canonical_netloc, query=urlencode(libpq_pairs, quote_via=quote)))
|
||||
|
||||
@staticmethod
|
||||
def _libpq_tls_params(
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,78 @@ class TestStripPrismaQueryParams:
|
|||
|
||||
return dict(parse_qsl(urlparse(url).query))
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url,expected_user,expected_password,expected_host,expected_port,expected_sslmode",
|
||||
(
|
||||
(
|
||||
"postgresql://u:wr ong'pw9@writer:5433/litellm",
|
||||
"u",
|
||||
"wr ong'pw9",
|
||||
"writer",
|
||||
"5433",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"postgresql://u:wr ong'pw9@writer:5433/litellm?schema=x&sslmode=require",
|
||||
"u",
|
||||
"wr ong'pw9",
|
||||
"writer",
|
||||
"5433",
|
||||
"require",
|
||||
),
|
||||
(
|
||||
"postgresql://u:p%40ss%20w@writer:5433/litellm",
|
||||
"u",
|
||||
"p@ss w",
|
||||
"writer",
|
||||
"5433",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"postgresql://u:p%z@writer:5433/litellm",
|
||||
"u",
|
||||
"p%z",
|
||||
"writer",
|
||||
"5433",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"postgresql://u:wr ong'pw9@[::1]:5433/litellm",
|
||||
"u",
|
||||
"wr ong'pw9",
|
||||
"::1",
|
||||
"5433",
|
||||
None,
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_userinfo_is_encoded_for_libpq(
|
||||
self,
|
||||
url: str,
|
||||
expected_user: str,
|
||||
expected_password: str,
|
||||
expected_host: str,
|
||||
expected_port: str,
|
||||
expected_sslmode: str | None,
|
||||
) -> None:
|
||||
from psycopg.conninfo import conninfo_to_dict
|
||||
|
||||
result: Final = ProxyExtrasDBManager._strip_prisma_query_params(url)
|
||||
conninfo: Final = conninfo_to_dict(result)
|
||||
|
||||
assert conninfo["user"] == expected_user
|
||||
assert conninfo["password"] == expected_password
|
||||
assert conninfo["host"] == expected_host
|
||||
assert conninfo["port"] == expected_port
|
||||
assert conninfo["dbname"] == "litellm"
|
||||
assert conninfo.get("sslmode") == expected_sslmode
|
||||
assert "schema" not in conninfo
|
||||
|
||||
def test_empty_password_userinfo_keeps_its_separator(self) -> None:
|
||||
url: Final = "postgresql://u:@writer/litellm"
|
||||
|
||||
assert ProxyExtrasDBManager._strip_prisma_query_params(url) == url
|
||||
|
||||
def test_prisma_ca_sslcert_becomes_sslrootcert_with_verify_full(self):
|
||||
url = "postgresql://u:p@writer:5432/db?schema=public&sslmode=require&sslcert=/tmp/pinned.pem&sslaccept=strict"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue