All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI / openapi-codegen-check (pull_request) Successful in 3m10s
CI Trade-In / backend-tests (pull_request) Successful in 5m47s
CI / backend-tests (pull_request) Successful in 18m3s
Ключ шифрования кук и сами куки уезжали в GlitchTip: сервисы сессий передают их bind-параметрами в pgp_sym_encrypt(:cookies_json, :key), а SQLAlchemy при ошибке печатает ВСЕ параметры в тексте StatementError. Правка на уровне движка (backend + tradein-mvp: db.py, auth_db.py, alembic/env.py) кроет все сайты вызова разом, включая четвёртую копию в scraper-kit и всё будущее. scheduler_main.py был единственным из трёх sentry_sdk.init без include_local_variables=False — процесс скрейпера, в кадрах лежат прокси-креды. НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением) и сырые psycopg-подключения мимо движков — отдельный класс. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.2 KiB
Python
47 lines
2.2 KiB
Python
"""#3194: bind-параметры не должны попадать в текст исключения (→ GlitchTip).
|
||
|
||
Через них уезжали ключ шифрования кук и сами куки: сервисы сессий зовут
|
||
`db.execute(text("... pgp_sym_encrypt(:cookies_json, :key) ..."), {...})`, а
|
||
SQLAlchemy при ошибке оборачивает всё в StatementError и печатает ВСЕ
|
||
параметры в `str(exc)`.
|
||
|
||
Отказ здесь обязан произойти НА УРОВНЕ ЗАПРОСА: если соединение падает на
|
||
CONNECT, до bind-параметров дело не доходит и тест зелёный по построению.
|
||
Поэтому — живой sqlite в памяти и обращение к несуществующей таблице.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
from sqlalchemy import create_engine, text
|
||
from sqlalchemy.exc import StatementError
|
||
|
||
SECRET = "sup3r-secret-cookie-key-3194"
|
||
|
||
|
||
def _provoke_statement_error(*, hide: bool) -> str:
|
||
engine = create_engine("sqlite://", hide_parameters=hide)
|
||
with engine.connect() as conn:
|
||
with pytest.raises(StatementError) as excinfo:
|
||
conn.execute(text("SELECT :key FROM no_such_table_3194"), {"key": SECRET})
|
||
return str(excinfo.value)
|
||
|
||
|
||
def test_bind_parameters_hidden_in_exception_text() -> None:
|
||
message = _provoke_statement_error(hide=True)
|
||
# Отказ именно на запросе, а не на соединении — иначе проверка ниже пуста.
|
||
assert "no_such_table_3194" in message
|
||
assert SECRET not in message
|
||
|
||
|
||
def test_falsification_secret_leaks_without_the_flag() -> None:
|
||
"""Обратная сторона: без флага секрет ВИДЕН — тест выше не тавтология."""
|
||
message = _provoke_statement_error(hide=False)
|
||
assert SECRET in message
|
||
|
||
|
||
def test_app_engine_hides_parameters() -> None:
|
||
"""Сам прод-движок, а не только принцип: create_engine ленив, коннекта нет."""
|
||
from app.core.db import engine
|
||
|
||
assert engine.hide_parameters is True
|