fix(tradein): секрет вебхука GlitchTip не попадает в access-log — скруббер query-секретов + приём из заголовка #3353

Merged
bot-backend merged 2 commits from fix/3154-glitchtip-secret-log-leak into main 2026-09-05 18:13:00 +00:00
2 changed files with 15 additions and 4 deletions
Showing only changes of commit 55a67e5c45 - Show all commits

View file

@ -20,10 +20,12 @@ from __future__ import annotations
import logging import logging
import re import re
# Имена параметров, значение которых маскируем. Значение — до следующего `&`, # Имена параметров, значение которых маскируем: имя ОКАНЧИВАЕТСЯ на чувствительное
# пробела или кавычки (access-строка uvicorn обрамляет запрос кавычками). # слово, поэтому перед альтернацией допускаем префикс (`client_secret`,
# `refresh_token`, `webhook_secret`). Значение — до следующего `&`, пробела или
# кавычки (access-строка uvicorn обрамляет запрос кавычками).
_SENSITIVE_QUERY = re.compile( _SENSITIVE_QUERY = re.compile(
r"([?&](?:secret|token|api[-_]?key|apikey|access[-_]?token|password|signature|sig)=)" r"([?&][\w.-]*(?:secret|token|api[-_]?key|apikey|access[-_]?token|password|signature|sig)=)"
r"[^&\s\"'<>]+", r"[^&\s\"'<>]+",
re.IGNORECASE, re.IGNORECASE,
) )

View file

@ -91,6 +91,11 @@ def test_app_logger_via_root_handler_masks_secret() -> None:
f"/hook?api_key={_SECRET}", f"/hook?api_key={_SECRET}",
f"/hook?apiKey={_SECRET}", f"/hook?apiKey={_SECRET}",
f'"GET /hook?access_token={_SECRET} HTTP/1.1"', f'"GET /hook?access_token={_SECRET} HTTP/1.1"',
# Имя с префиксом: чувствительное слово в КОНЦЕ имени параметра.
f"/hook?client_secret={_SECRET}",
f"/hook?webhook_secret={_SECRET}",
f"/hook?refresh_token={_SECRET}",
f"/hook?auth_token={_SECRET}",
], ],
) )
def test_sensitive_param_names_are_masked(raw: str) -> None: def test_sensitive_param_names_are_masked(raw: str) -> None:
@ -104,7 +109,11 @@ def test_sensitive_param_names_are_masked(raw: str) -> None:
[ [
"GET /api/v1/trade-in/offers?limit=50&city=Екатеринбург", "GET /api/v1/trade-in/offers?limit=50&city=Екатеринбург",
"https://metrics.gendsgn.ru/ingest/loki/api/v1/push", "https://metrics.gendsgn.ru/ingest/loki/api/v1/push",
"/hook?not_a_secret_name=value", # Имя НЕ оканчивается на чувствительное слово — маскировать нечего.
"/hook?secretary=anna",
"/hook?tokens_page=2",
# `token-info` в ПУТИ, а не в query: значения там нет вовсе.
"GET /api/v1/token-info?limit=5",
], ],
) )
def test_innocent_lines_untouched(raw: str) -> None: def test_innocent_lines_untouched(raw: str) -> None: