From 3b4da7e0fd048ed9553e0670410ca121b31df713 Mon Sep 17 00:00:00 2001 From: Ashesh Vashi Date: Sun, 7 Jun 2026 10:15:28 +0530 Subject: [PATCH] fix(docker): read PGADMIN_CONFIG_CONFIG_DATABASE_URI safely via os.environ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes pgadmin-org/pgadmin4#9984. The entrypoint constructed a Python `-c` argument by shell-substituting `${PGADMIN_CONFIG_CONFIG_DATABASE_URI}` inside a double-quoted Python string: val = check_external_config_db("${PGADMIN_CONFIG_CONFIG_DATABASE_URI}") That collided with the long-standing config_distro.py convention, which requires the env var's value to BE a Python literal (so it produces valid Python when the entrypoint appends `CONFIG_DATABASE_URI = $val` to config_distro.py). Users therefore set the env var to `'postgresql+psycopg://...'` — and the entrypoint then re-wrapped the literal in another set of quotes, producing `check_external_config_db("'postgresql+psycopg://...'")` — a string with literal single quotes inside, which SQLAlchemy cannot parse: sqlalchemy.exc.ArgumentError: Could not parse SQLAlchemy URL from given URL string The Python crash made `$(...)` capture an empty string, so the downstream check at `[ "${external_config_db_exists}" = "False" ]` also failed — silently skipping the entire first-launch user-setup block. That is the second symptom on the issue: PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD getting ignored. This was a regression from commit 1fe840fca (Oct 2024, docker secrets support), which incidentally added `"..."` shell quoting around the expansion in an otherwise reasonable PR. The original 2024-09 form (bare `${VAR}` expansion) relied on the Python-literal convention and worked correctly. Rather than restore the bare-expansion form (which is a 7-year Chesterton's fence that the next shellcheck-style cleanup would reintroduce), remove the shell from the quoting question altogether: - Read the env var inside Python via `os.environ` — the shell no longer participates in Python-literal handling. - Use `ast.literal_eval` to unwrap the legacy `'url'` / `"url"` form when present; on `ValueError`/`SyntaxError` fall through to the raw value so users who set the env var without quotes also work. - Default `external_config_db_exists` stays "False" on any Python failure (only overwritten when the Python call produces non-empty stdout). This fixes the secondary PGADMIN_DEFAULT_EMAIL-ignored regression without any other change. Manual verification: - All three env-var forms (`'url'`, `"url"`, raw `url`) now parse to the same clean URI and round-trip through check_external_config_db correctly. - Both branches verified against a local Postgres 17 — returns False when no `server` table exists, True after seeding one. --- pkg/docker/entrypoint.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index c40987e5f9f..7a027f423db 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -195,9 +195,28 @@ EOF fi # Check whether the external configuration database exists if it is being used. +# +# The URI is read inside Python via os.environ — the shell no longer +# participates in Python-literal quoting. `ast.literal_eval` unwraps the +# legacy `'url'` form that the config_distro.py generation convention +# requires; raw values pass through unchanged. On any Python failure the +# "False" default below is preserved so first-launch user setup still +# runs (see #9984). external_config_db_exists="False" if [ -n "${PGADMIN_CONFIG_CONFIG_DATABASE_URI}" ]; then - external_config_db_exists=$(cd /pgadmin4/pgadmin/utils && $SU_EXEC /venv/bin/python3 -c "from check_external_config_db import check_external_config_db; val = check_external_config_db(\"${PGADMIN_CONFIG_CONFIG_DATABASE_URI}\"); print(val)") + result=$(cd /pgadmin4/pgadmin/utils && $SU_EXEC /venv/bin/python3 -c " +import os, ast +from check_external_config_db import check_external_config_db +raw = os.environ['PGADMIN_CONFIG_CONFIG_DATABASE_URI'] +try: + uri = ast.literal_eval(raw) +except (ValueError, SyntaxError): + uri = raw +print(check_external_config_db(uri)) +" 2>/dev/null) + if [ -n "$result" ]; then + external_config_db_exists="$result" + fi fi # DRY of the code to load the PGADMIN_SERVER_JSON_FILE