fix(tradein): токен DaData не утекает в лог + фикс crash access-log скраббера #3514

Merged
lekss361 merged 1 commit from fix/3471-mask-secrets-in-logs into main 2026-09-13 10:52:03 +00:00
Owner

Summary

  • DaData на 403 возвращает "Feature 'CLEAN' disabled for token '<действующий токен>'" открытым текстом — body_preview в app/services/dadata.py копировал это в WARNING/ERROR лог как есть, токен утекал в docker logs. Добавлен scrub_body_secrets() в app/core/log_scrub.py (двухслойно: token '<value>' + generic hex/base64 24+ символов) и применён на всех трёх местах, где response.text попадает в лог (403/401 и generic 4xx в clean_address/suggest_addresses).
  • Попутно найден и исправлен источник прод-краша --- Logging error --- / ValueError: not enough values to unpack (expected 5, got 0): QuerySecretFilter (#3154) при найденном секрете схлопывал record.args в (), а uvicorn.logging.AccessFormatter.formatMessage() распаковывает record.args напрямую как 5-tuple в обход record.getMessage() — сама попытка заскрабить секрет в access-логе ломала форматирование записи. Фильтр переписан на поэлементный скраб record.args (arity не трогается).

Test plan

  • tests/services/test_dadata.py — новый test_clean_address_masks_token_in_feature_disabled_log (реалистичный 40-символьный fake-токен), падает на исходном коде (проверено вручную откатом фикса), зелёный после фикса; полный файл 46/46 passed.
  • tests/test_3154_query_secret_scrub.py — новый test_uvicorn_access_formatter_survives_secret_scrub через настоящий uvicorn.logging.AccessFormatter; падает на пред-фикс версии фильтра (проверено вручную откатом), зелёный после фикса; полный файл 20/20 passed.
  • ruff check — чисто на всех 4 изменённых файлах.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG

## Summary - DaData на 403 возвращает "Feature 'CLEAN' disabled for token '<действующий токен>'" открытым текстом — `body_preview` в `app/services/dadata.py` копировал это в WARNING/ERROR лог как есть, токен утекал в docker logs. Добавлен `scrub_body_secrets()` в `app/core/log_scrub.py` (двухслойно: `token '<value>'` + generic hex/base64 24+ символов) и применён на всех трёх местах, где `response.text` попадает в лог (403/401 и generic 4xx в `clean_address`/`suggest_addresses`). - Попутно найден и исправлен источник прод-краша `--- Logging error ---` / `ValueError: not enough values to unpack (expected 5, got 0)`: `QuerySecretFilter` (#3154) при найденном секрете схлопывал `record.args` в `()`, а `uvicorn.logging.AccessFormatter.formatMessage()` распаковывает `record.args` напрямую как 5-tuple в обход `record.getMessage()` — сама попытка заскрабить секрет в access-логе ломала форматирование записи. Фильтр переписан на поэлементный скраб `record.args` (arity не трогается). ## Test plan - [x] `tests/services/test_dadata.py` — новый `test_clean_address_masks_token_in_feature_disabled_log` (реалистичный 40-символьный fake-токен), падает на исходном коде (проверено вручную откатом фикса), зелёный после фикса; полный файл 46/46 passed. - [x] `tests/test_3154_query_secret_scrub.py` — новый `test_uvicorn_access_formatter_survives_secret_scrub` через настоящий `uvicorn.logging.AccessFormatter`; падает на пред-фикс версии фильтра (проверено вручную откатом), зелёный после фикса; полный файл 20/20 passed. - [x] `ruff check` — чисто на всех 4 изменённых файлах. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
lekss361 added 1 commit 2026-09-13 10:36:15 +00:00
fix(tradein): токен DaData не утекает в лог + фикс crash в access-log скраббере
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m49s
d7ab12e8a6
DaData на 403 возвращает "Feature CLEAN disabled for token '<действующий токен>'."
открытым текстом — body_preview копировал это в WARNING/ERROR лог как есть,
токен уезжал в docker logs (потенциальный breadcrumb в GlitchTip). Добавлен
scrub_body_secrets() в app/core/log_scrub.py: маскирует token '<value>' и любую
generic hex/base64-подобную последовательность 24+ символов — защита не зависит
от точной формулировки вендора.

Заодно найден и исправлен источник "--- Logging error ---" / ValueError: not
enough values to unpack (expected 5, got 0) из прод-логов tradein-backend:
QuerySecretFilter (#3154) при найденном секрете в query-строке схлопывал
record.args в (), а uvicorn.logging.AccessFormatter.formatMessage() распаковывает
record.args напрямую как 5-tuple в обход record.getMessage() — сама попытка
заскрабить секрет ронла форматирование access-лога. Фильтр переписан на
поэлементный скраб record.args (arity не трогается).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
lekss361 merged commit ed76ad8d43 into main 2026-09-13 10:52:03 +00:00
lekss361 deleted branch fix/3471-mask-secrets-in-logs 2026-09-13 10:52:04 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3514
No description provided.