From c229b01b0e5724f3cd5d4573e29ec896366b1ed5 Mon Sep 17 00:00:00 2001 From: Herdiyan Adam Putra Date: Sun, 26 Jul 2026 22:07:30 +0700 Subject: [PATCH] Redact password from DatabaseSessionService engine-creation errors DatabaseSessionService.__init__ embedded the raw db_url in three ValueError messages raised when engine creation fails (invalid URL argument, missing driver module, or any other error). A SQLAlchemy URL for a networked backend carries the password inline, e.g. postgresql+asyncpg://user:password@host/db, so any of these failures (for example a driver that is not installed) surfaced the database password verbatim in the exception and in any logs that record it. Render the URL with hide_password=True before including it in the messages, falling back to a redacted placeholder when the URL cannot be parsed. The messages keep the useful dialect/host/database context while no longer exposing the password. --- .../adk/sessions/database_session_service.py | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/google/adk/sessions/database_session_service.py b/src/google/adk/sessions/database_session_service.py index 6c3572b8d66..eb4b8e5134d 100644 --- a/src/google/adk/sessions/database_session_service.py +++ b/src/google/adk/sessions/database_session_service.py @@ -70,6 +70,22 @@ logger = logging.getLogger("google_adk." + __name__) + +def _redact_db_url(db_url: str) -> str: + """Returns the database URL with any embedded password removed. + + The engine-creation error messages below include the database URL to aid + debugging, but a URL may embed credentials + (e.g. ``postgresql://user:password@host/db``). Rendering it with + ``hide_password=True`` keeps the useful parts (dialect, host, database) while + preventing the password from leaking into exceptions and logs. + """ + try: + return make_url(db_url).render_as_string(hide_password=True) + except Exception: # noqa: BLE001 - fall back to a fully redacted placeholder + return "[redacted]" + + _STALE_SESSION_ERROR_MESSAGE = ( "The session has been modified in storage since it was loaded. " "Please reload the session before appending more events." @@ -284,16 +300,17 @@ def __init__( event.listen(db_engine.sync_engine, "connect", _set_sqlite_pragma) except Exception as e: + safe_db_url = _redact_db_url(db_url) if isinstance(e, ArgumentError): raise ValueError( - f"Invalid database URL format or argument '{db_url}'." + f"Invalid database URL format or argument '{safe_db_url}'." ) from e if isinstance(e, ImportError): raise ValueError( - f"Database related module not found for URL '{db_url}'." + f"Database related module not found for URL '{safe_db_url}'." ) from e raise ValueError( - f"Failed to create database engine for URL '{db_url}'" + f"Failed to create database engine for URL '{safe_db_url}'" ) from e else: self._owns_db_engine = False