Compare commits
No commits in common. "de31f2791b310da268c35f4ffe6534ab03abb904" and "65dfdee63d043d388b831e7d760b8ed5dcfaa734" have entirely different histories.
de31f2791b
...
65dfdee63d
7 changed files with 3 additions and 101 deletions
|
|
@ -43,12 +43,6 @@ def run_migrations_online() -> None:
|
||||||
config.get_section(config.config_ini_section, {}),
|
config.get_section(config.config_ini_section, {}),
|
||||||
prefix="sqlalchemy.",
|
prefix="sqlalchemy.",
|
||||||
poolclass=pool.NullPool,
|
poolclass=pool.NullPool,
|
||||||
# #3194: SQLAlchemy печатает ВСЕ bind-параметры в тексте StatementError.
|
|
||||||
# Миграции гоняют DDL/DML с литералами и параметрами из данных — флаг
|
|
||||||
# на уровне движка не даёт им уехать в GlitchTip.
|
|
||||||
# НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со
|
|
||||||
# значением) и сырые psycopg-подключения мимо движков.
|
|
||||||
hide_parameters=True,
|
|
||||||
)
|
)
|
||||||
with connectable.connect() as connection:
|
with connectable.connect() as connection:
|
||||||
context.configure(
|
context.configure(
|
||||||
|
|
|
||||||
|
|
@ -139,13 +139,6 @@ def _build() -> tuple[Engine, sessionmaker[Session]]:
|
||||||
future=True,
|
future=True,
|
||||||
pool_timeout=3,
|
pool_timeout=3,
|
||||||
connect_args={"connect_timeout": 3, "options": "-c statement_timeout=3000"},
|
connect_args={"connect_timeout": 3, "options": "-c statement_timeout=3000"},
|
||||||
# #3194: SQLAlchemy печатает ВСЕ bind-параметры в тексте StatementError —
|
|
||||||
# через них в GlitchTip уезжали ключ шифрования кук и сами куки
|
|
||||||
# (pgp_sym_encrypt(:cookies_json, :key)). Флаг на УРОВНЕ ДВИЖКА кроет все
|
|
||||||
# сайты вызова разом, включая будущие.
|
|
||||||
# НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением)
|
|
||||||
# и сырые psycopg-подключения мимо движков — это отдельный класс.
|
|
||||||
hide_parameters=True,
|
|
||||||
)
|
)
|
||||||
except (ArgumentError, ValueError):
|
except (ArgumentError, ValueError):
|
||||||
# ValueError — не паранойя: на «почти URL» разбор SQLAlchemy доходит до
|
# ValueError — не паранойя: на «почти URL» разбор SQLAlchemy доходит до
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,7 @@ from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(settings.database_url, pool_pre_ping=True, future=True)
|
||||||
settings.database_url,
|
|
||||||
pool_pre_ping=True,
|
|
||||||
future=True,
|
|
||||||
# #3194: SQLAlchemy печатает ВСЕ bind-параметры в тексте StatementError —
|
|
||||||
# через них в GlitchTip уезжали ключ шифрования кук и сами куки
|
|
||||||
# (pgp_sym_encrypt(:cookies_json, :key)). Флаг на УРОВНЕ ДВИЖКА кроет все
|
|
||||||
# сайты вызова разом, включая будущие.
|
|
||||||
# НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением)
|
|
||||||
# и сырые psycopg-подключения мимо движков — это отдельный класс.
|
|
||||||
hide_parameters=True,
|
|
||||||
)
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False)
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,18 +90,7 @@ def _build() -> tuple[Engine, sessionmaker[Session]]:
|
||||||
if not dsn:
|
if not dsn:
|
||||||
raise AuthDatabaseNotConfiguredError(_NOT_CONFIGURED_MSG)
|
raise AuthDatabaseNotConfiguredError(_NOT_CONFIGURED_MSG)
|
||||||
try:
|
try:
|
||||||
engine = create_engine(
|
engine = create_engine(dsn, pool_pre_ping=True, future=True)
|
||||||
dsn,
|
|
||||||
pool_pre_ping=True,
|
|
||||||
future=True,
|
|
||||||
# #3194: SQLAlchemy печатает ВСЕ bind-параметры в тексте StatementError —
|
|
||||||
# через них в GlitchTip уезжали ключ шифрования кук и сами куки
|
|
||||||
# (pgp_sym_encrypt(:cookies_json, :key)). Флаг на УРОВНЕ ДВИЖКА кроет все
|
|
||||||
# сайты вызова разом, включая будущие.
|
|
||||||
# НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением)
|
|
||||||
# и сырые psycopg-подключения мимо движков — это отдельный класс.
|
|
||||||
hide_parameters=True,
|
|
||||||
)
|
|
||||||
except (ArgumentError, ValueError):
|
except (ArgumentError, ValueError):
|
||||||
# ValueError — не паранойя: на «почти URL» разбор SQLAlchemy доходит до
|
# ValueError — не паранойя: на «почти URL» разбор SQLAlchemy доходит до
|
||||||
# `int(port)` и падает с `invalid literal for int() with base 10: 'w'`,
|
# `int(port)` и падает с `invalid literal for int() with base 10: 'w'`,
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,7 @@ from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(settings.database_url, pool_pre_ping=True, future=True)
|
||||||
settings.database_url,
|
|
||||||
pool_pre_ping=True,
|
|
||||||
future=True,
|
|
||||||
# #3194: SQLAlchemy печатает ВСЕ bind-параметры в тексте StatementError —
|
|
||||||
# через них в GlitchTip уезжали ключ шифрования кук и сами куки
|
|
||||||
# (pgp_sym_encrypt(:cookies_json, :key)). Флаг на УРОВНЕ ДВИЖКА кроет все
|
|
||||||
# сайты вызова разом, включая будущие.
|
|
||||||
# НЕ закрывает: текст ошибки самого драйвера (Postgres DETAIL со значением)
|
|
||||||
# и сырые psycopg-подключения мимо движков — это отдельный класс.
|
|
||||||
hide_parameters=True,
|
|
||||||
)
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False)
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,6 @@ if settings.glitchtip_dsn:
|
||||||
release=os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown",
|
release=os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown",
|
||||||
traces_sample_rate=0.0,
|
traces_sample_rate=0.0,
|
||||||
send_default_pii=False,
|
send_default_pii=False,
|
||||||
# #3194: процесс скрейпера — в локальных переменных кадров лежат
|
|
||||||
# прокси-креды вида user:pass, default sentry_sdk (True) приложил бы их
|
|
||||||
# к traceback открытым текстом. Так же выставлено у соседей (main.py,
|
|
||||||
# tgbot). НЕ закрывает значения в самом тексте исключения и в логах.
|
|
||||||
include_local_variables=False,
|
|
||||||
before_send=_before_send,
|
before_send=_before_send,
|
||||||
before_send_transaction=_before_send,
|
before_send_transaction=_before_send,
|
||||||
integrations=[
|
integrations=[
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
"""#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
|
|
||||||
Loading…
Add table
Reference in a new issue