From da7814810c34aba4b45f0383596e13002624b123 Mon Sep 17 00:00:00 2001 From: lekss361 Date: Sun, 24 May 2026 14:03:15 +0300 Subject: [PATCH 1/2] =?UTF-8?q?fix(deploy):=20tradein=5Ffdw=5Freader=20pas?= =?UTF-8?q?sword=20=E2=80=94=20psql=20var=20interpolation=20in=20DO=20bloc?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit psql :'pw' substitution НЕ работает внутри dollar-quoted блока $$..$$ — это правило psql, не bug. Symptom: deploy 2026-05-24 после merge PR #503 упал с 'syntax error at or near :' on LINE 4 of set_tradein_fdw_password.sql, :'pw' дошёл до сервера как literal вместо интерполяции. Fix: передаём password в DO через session GUC (set_config) — psql интерполирует :'pw' ВНЕ dollar quote, внутри читаем через current_setting(). Очищаем GUC после выполнения (defense-in-depth). --- ops/db-bootstrap/set_tradein_fdw_password.sql | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ops/db-bootstrap/set_tradein_fdw_password.sql b/ops/db-bootstrap/set_tradein_fdw_password.sql index c0742ab2..0daf946a 100644 --- a/ops/db-bootstrap/set_tradein_fdw_password.sql +++ b/ops/db-bootstrap/set_tradein_fdw_password.sql @@ -10,18 +10,26 @@ -- Combined with the regex whitelist on the backend side (fdw.py _PASSWORD_RE) -- this gives defense-in-depth. -- --- Extracted from deploy.yml inline DO block (PR #493 deploy/1156 incident). --- The original block used multi-line backslash continuations inside a double-quoted --- bash string with $$-dollar-quoting + psql variable substitution — fragile escaping --- that likely caused the bootstrap step to fail before `compose up -d` ran. --- Using `-f ` with psql-native variable substitution via `-v pw=` is safe. +-- psql variable substitution (:'pw') НЕ интерполируется внутри dollar-quoted +-- блока ($$...$$) — это правило psql, не bug. Поэтому password передаём в DO +-- через сессионный GUC (set_config), который psql интерполирует ВНЕ dollar +-- quote, и читаем внутри через current_setting(). +-- Reference incident: deploy 2026-05-24 (post-merge PR #503) упал на +-- "syntax error at or near ':'" в LINE 4 этого файла — :'pw' дошёл до сервера +-- as literal вместо интерполяции. + +SELECT set_config('app.fdw_pw', :'pw', false); DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'tradein_fdw_reader') THEN - EXECUTE format('ALTER ROLE tradein_fdw_reader WITH PASSWORD %L', :'pw'); + EXECUTE format('ALTER ROLE tradein_fdw_reader WITH PASSWORD %L', current_setting('app.fdw_pw')); RAISE NOTICE 'tradein_fdw_reader password set'; ELSE RAISE NOTICE 'tradein_fdw_reader role missing — migration 100_tradein_fdw_role.sql not applied yet'; END IF; END $$; + +-- Clear GUC after use (defense-in-depth — не оставляем password в session state +-- даже на short connection). +SELECT set_config('app.fdw_pw', '', false); -- 2.45.3 From 08a8cf51b362b184b78a0f8ed5e35ce884b192da Mon Sep 17 00:00:00 2001 From: lekss361 Date: Sun, 24 May 2026 14:21:36 +0300 Subject: [PATCH 2/2] =?UTF-8?q?fix(deploy):=20mute=20psql=20set=5Fconfig?= =?UTF-8?q?=20stdout=20=E2=80=94=20prevent=20password=20leak=20in=20CI=20l?= =?UTF-8?q?ogs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review-bot PR #510 нашёл P0: `set_config(name,value,is_local)` возвращает установленное значение, psql печатает rows на stdout без `-q`/`-A`/`-t`, итог — фактический pw появляется в Forgejo Actions deploy logs (retained, visible всем с repo read access). Fix: оборачиваем оба `SELECT set_config(...)` в `\o /dev/null` / `\o` brackets. Output mutes только для этих строк — NOTICE-сообщения из DO block (idempotency signal 'tradein_fdw_reader password set') остаются видимыми. Также добавлен rollback hint в комментарии: НЕ revert файл (вернёт сломанный :'pw' внутри $$), а unset GENDESIGN_FDW_PASSWORD в .env.runtime на VPS. --- ops/db-bootstrap/set_tradein_fdw_password.sql | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ops/db-bootstrap/set_tradein_fdw_password.sql b/ops/db-bootstrap/set_tradein_fdw_password.sql index 0daf946a..459fb823 100644 --- a/ops/db-bootstrap/set_tradein_fdw_password.sql +++ b/ops/db-bootstrap/set_tradein_fdw_password.sql @@ -17,8 +17,21 @@ -- Reference incident: deploy 2026-05-24 (post-merge PR #503) упал на -- "syntax error at or near ':'" в LINE 4 этого файла — :'pw' дошёл до сервера -- as literal вместо интерполяции. +-- +-- ⚠️ `set_config(name, value, is_local) -> text` ВОЗВРАЩАЕТ установленное +-- значение. Без `\o /dev/null` бы psql напечатал password на stdout → leak в +-- Forgejo Actions deploy logs (retained, visible всем с repo read access). +-- Поэтому оборачиваем оба set_config вызова в `\o /dev/null` / `\o` brackets +-- — output mutes только для этих строк, NOTICE-сообщения из DO block +-- (idempotency signal) остаются видимыми. +-- +-- Rollback path: НЕ revert этого файла (вернёт сломанный `:'pw'` внутри $$). +-- Корректный rollback — unset GENDESIGN_FDW_PASSWORD в /opt/gendesign/backend +-- /.env.runtime на VPS, deploy.yml тогда пропустит этот шаг полностью. +\o /dev/null SELECT set_config('app.fdw_pw', :'pw', false); +\o DO $$ BEGIN @@ -31,5 +44,8 @@ BEGIN END $$; -- Clear GUC after use (defense-in-depth — не оставляем password в session state --- даже на short connection). +-- даже на short connection). Same \o trick — set_config return value is empty +-- string here, но всё равно лишний row в stdout не нужен. +\o /dev/null SELECT set_config('app.fdw_pw', '', false); +\o -- 2.45.3