fix(deploy): tradein_fdw_reader password — psql var interpolation in DO block #510
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#510
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-fdw-psql-var-in-do"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
ops/db-bootstrap/set_tradein_fdw_password.sql: pass password в DO block через session GUC (set_config) вместо прямого psql:'pw'substitution.Root cause
psql variable substitution (
:'pw') не интерполируется внутри dollar-quoted блока ($$...$$) — это документированное поведение psql, не bug (psql docs: "Variable interpolation will not be performed within quoted SQL literals and identifiers").Текущий код:
:'pw'доходит до сервера как literal →syntax error at or near ":".Symptom
Deploy 2026-05-24 после merge PR #503 (DOM.РФ WAF fix) упал:
Это блокирует
docker compose up -d→ backend остаётся на старом образе → PR #503 fix не применился к работающему scraper'у. Этот же баг будет валить любой следующий deploy.Fix
Логика идентична оригиналу: idempotent (skip если роль отсутствует),
format(%L)экранирует password как SQL literal. Только канал передачи параметра меняется.Test plan
tradein_fdw_reader password set)docker compose exec postgres psql -U postgres -d gendesign -c "\du tradein_fdw_reader"— роль существует, pwd actualRisk: LOW
set_config app.fdw_pw '' false) убирает password из session GUC после executeIF EXISTS+ role rename невозможен послеALTERбез cross-check):), либо deploy этот код впервые исполняет именно сейчас. Текущий incident — первый раз когда file применяется. После fix — стандартныйpsql -fflow.Deep Code Review — verdict: CHANGES REQUESTED
Summary
🔴 Critical (BLOCK) — psql leaks the password to deploy logs
ops/db-bootstrap/set_tradein_fdw_password.sql:18(и :35 для cleanup):set_config(name, value, is_local) -> textвозвращает установленное значение. psql по умолчанию печатает result rows на stdout без-q/-A/-t/\o. В.forgejo/workflows/deploy.yml:262-264вызов:результат:
-> password окажется в Forgejo Actions runner output -> run logs retained -> visible всем кто имеет repo read access.
GENDESIGN_FDW_PASSWORDприходит из.env.runtimeна VPS, не registered как Forgejo secret -> автоматического маскирования нет.Fix (один из вариантов, ranked):
\oredirect внутри SQL (минимальная диффа, без изменений в workflow):deploy.yml):Но тогда NOTICE messages (
tradein_fdw_reader password set) тоже глотаются — теряем diagnostic signal. Менее предпочтительно.Хакерство, читается хуже варианта 1.
Рекомендую вариант 1 — explicit, локально в SQL файле, NOTICE-сообщения сохраняются.
🟢 Положительное
:'pw'не интерполируется внутри$$...$$(документированное поведение psql, не bug).format('... %L', current_setting(...))—%Lэкранирует password как SQL literal, SQL injection-safe.IF EXISTSguard для роли).set_config('app.fdw_pw', '', false)в конце — defense-in-depth (хотя session GUC и так умирает с psql exit).-v pw=$GENDESIGN_FDW_PASSWORD.is_local=falseкорректно (нет окружающей транзакции;trueпотребовал бы BEGIN/COMMIT).🟡 Minor
app.fdw_pw— namespaceapp.*в PG 9.2+ не требует регистрации (любой dotted GUC принимается). OK.ON_ERROR_STOP=on— но session всё равно закрывается -> GUC исчезает. Acceptable.:'pw'внутри$$), а unsetGENDESIGN_FDW_PASSWORDв.env.runtimeчтобы пропустить шаг. Стоит упомянуть в комментарии файла.Cross-file impact
.forgejo/workflows/deploy.yml:254-273— invoker без изменений (правильно, psql stdin redirect уже использовался). Если применяется вариант fix #1 — workflow не трогаем.data/sql/100_tradein_fdw_role.sql— создаёт роль passwordless, comment корректно ссылается на этот bootstrap. Не затрагивается.tradein-mvp/backend/app/core/fdw.py_PASSWORD_RE— backend whitelist для password в connection string. Этот PR его не меняет.tradein-mvp/backend/app/core/config.py— читает env, не затрагивается.Verdict
CHANGES REQUESTED — добавь
\o /dev/null/\obracket вокруг двухSELECT set_config(...)строк. После этого fix логически correct и можно мержить. Текущая версия предотвращает syntax error, но открывает leak password в CI logs — net regression по security.🚨 Security-sensitive — human merge required after manual diff audit
Deep Code Review (re-review) — verdict: APPROVE
Fixup verification (
da78148->08a8cf5)Prior BLOCK:
SELECT set_config('app.fdw_pw', :'pw', false)-> password в Forgejo Actions deploy logs (no auto-masking,.env.runtimesource not registered as secret).Applied fix — exactly variant 1 from prior recommendation:
Lines 30-32 + 47-49. DO block между ними не оборачивается — NOTICE messages (
tradein_fdw_reader password set/... role missing) остаются visible в logs (diagnostic signal preserved).Verified properties
\o /dev/nullглушит ТОЛЬКО result rows из обёрнутых SELECT'ов — это psql client-side redirect, NOTICE-сообщения идут через другой канал (server messages) и не подпадают под\o.\o(без аргумента) восстанавливает stdout — корректное закрытие bracket.set_config('app.fdw_pw', '', false)тоже обёрнут (хотя возвращает empty string — defense in depth ради consistency).format(%L)SQL injection-safe — без regression.Cross-file impact (unchanged from prior review)
.forgejo/workflows/deploy.yml:254-273— не модифицирован (правильно, workflow-level redirect глотал бы NOTICE).data/sql/100_tradein_fdw_role.sql— passwordless role create, не затронут.tradein-mvp/backend/app/core/fdw.py_PASSWORD_RE— backend whitelist intact.Residual risk
Minor:
\o /dev/nullis POSIX-only (Linux runner). Если bootstrap когда-нибудь run будет on Windows runner —/dev/nullне существует. Текущий deploy runs on Linux VPS только, не блокирует merge.Verdict
APPROVE — fix applied correctly, log leak closed, diagnostic NOTICE-сообщения сохранены, no new regression. Net positive по security.
🚨 Security fix verified — human merge required after deploy diff audit (per git-pr.md security tripwire policy для PR на credential pipeline).