Files
2026-06-30 11:25:29 +04:00

5.0 KiB
Raw Permalink Blame History

Аудит кода contracts-flask — 2026-06-30

Контекст

После переименования services/compare/ и восстановления сервиса проведён аудит кодовой базы. Фокус: что реально упадёт, сломается или работает нестабильно. Без вылизывания. Сервис непубличный, дев-окружение. Auth пока не нужен.


⚠️ Исправлено в этом цикле

#1. POST-хендлеры без try вокруг Content-Length и json.loads

Файл: deploy/convert_server.py Проблема: _handle_api_sync, _handle_api_groups, _handle_apply_groups, промпт-хендлеры — все делают json.loads(self.rfile.read(length)) без try. Битый Content-Length → ValueError, пустой body → JSONDecodeError. Оба не ловятся → оборванное соединение. Фикс: обёрнуто в try/except с возвратом {"ok": false, "error": "..."}.

#11. ZIP-бомба в DrHider

Файл: deploy/drhider/drhider.py Проблема: проверяется только заявленный info.file_size (можно подделать), нет лимита фактически распакованных байт, нет проверки ratio сжатия. Фикс: скопирована защита из compare/unzip.py — накопительный счётчик распакованных байт + ratio-проверка (100×).

#6. classify сбрасывает status у уже классифицированных документов

Файл: deploy/compare/classify.py Проблема: reset_classify_status(batch_id) в начале classify_batch() сбрасывает на 'pending' ВСЕ документы батча, включая уже классифицированные. При повторном запуске — жжёт токены LLM на переклассификацию. Фикс: reset_classify_status теперь сбрасывает только 'processing' (crash recovery), оставляя 'classified' и 'garbage' нетронутыми.


🟡 Известные проблемы (не исправлены — низкий приоритет)

SSE: двойная запись в мёртвый сокет

convert_server.py — при отвале клиента run_pipeline бросает BrokenPipeError, except ловит и снова зовёт _sse → второе исключение. Грязные трейсы, но не крашит сервер.

classify: «успешный» JSON от LLM, который не dict

classify.pyjson.loads может вернуть список/строку вместо dict → AttributeError → документ молча помечается failed. Случается редко.

.doc через /upload не работает

parse.py зовёт _parse_docx (python-docx) для .doc, а тот читает только OOXML/.docx. Формат разрешён в upload.py, но не парсится. Надо либо убрать .doc из whitelist, либо звать /convert-doc.

Гонка счётчиков в ThreadPoolExecutor

classify.py мутирует garbage, json_total, type_counts из 4 потоков через nonlocal без локов. Счётчики могут привирать на 1-2 единицы.

_safe_json_parse — 4 уровня эвристик

Реаниматор битого JSON. Лечит симптом «LLM обрезал ответ». Дописывание кавычек/скобок вслепую может дать валидный но мусорный JSON → тихая неверная классификация.

Весь файл в base64 в БД

upload.py хранит original_bytes как base64 (+33%) в Postgres. SELECT * таскает мегабайты. База пухнет.

LLM URL/key захардкожены в 3 местах

classify.py, drhider_server.py, llm_client.py. Рассинхрон — тот же класс проблемы, что с DB_NAME.

DrHider: PDF→txt + потеря форматирования DOCX

PDF на выходе становится .txt. Замена в DOCX сваливает текст в первый run → форматирование плывёт.


Что НЕ является проблемой (проверено)

  • SQL-инъекций нет — весь db/ на параметризованных запросах
  • Path traversal в static-раздаче прикрыт realpath+startswith
  • Path traversal в ZIP-распаковке прикрыт (unzip.py, drhider.py)
  • Nginx защищает от больших тел (client_max_body_size)
  • DrHider двойной encode fix сделан