12 KiB
Аудит для Sonnet — пофайловый разбор
Ты должен прочитать КАЖДЫЙ файл из списка ниже и выдать по нему детальный отчёт.
Формат отчёта ПОФАЙЛОВО
Для каждого файла:
### convert_server.py
| Строка | Тип | Серьёзность | Что не так | Как исправить |
|--------|-----|-------------|------------|---------------|
| 242 | dead code | low | Дубликат _handle_cleanup | Удалить второй |
После пофайлового разбора — СВОДНАЯ ТАБЛИЦА всех проблем по серьёзности: critical → high → medium → low.
Файл 1: contractor/deploy/convert_server.py
Что это: HTTP роутер на http.server + ThreadingMixIn. Все endpoint'ы приложения.
Что смотреть:
- ВСЕ
do_GET,do_POST,do_DELETE— правильная диспетчеризация? Нет мёртвых путей? _handle_process_v2— валидация UUID? SSE корректно закрывается при ошибке? Утечка соединений?_handle_upload— размер тела? Content-Type проверка? Таумаут?_handle_cleanup— ДВА определения (строки ~242 и ~255). Второй перекрывает первый. Dead code. Порядок DELETE правильный?_handle_api_sync— читает JSON body. Что если тело пустое/битое? Что если keep_ids содержит 10000 id? Нет лимита._handle_api_document— doc_id из URL без валидации UUID → psycopg2.InvalidTextRepresentation на "fake-id"._handle_api_document_delete— cascade порядок: spec_current → spec_events → supplements → document. Правильный?_handle_classify_batch— batch_id из JSON без валидации UUID._handle_apply_groups— валидация структуры groups?_handle_api_prompts_*— role из query params без валидации._json()— экранирует ли спецсимволы в данных?_sse()— f-string с json.dumps. Данные от LLM могут содержать спецсимволы.- Импорт
executeна строке 12 — не конфликтует с другими импортами? ALTER TABLE ADD COLUMN IF NOT EXISTS— права на DDL? Идемпотентно?- CORS —
_send_cors()вызывается везде? OPTIONS?
Файл 2: contractor/deploy/app.js
Что это: Весь фронтенд (42KB). Загрузка, классификация, группы, сравнение, промпты.
Что смотреть:
fileQueue,contractId,batchId— глобальные. Где расходятся с сервером?renderTable()—innerHTMLизfileQueue[].name,fileQueue[].status. XSS?fileInput change— гонка удаления старых + upload новых?syncDB()— fire-and-forget, без await, без проверки ответа.runClassify()— утечка таймеров при ошибке?loadGroups()— ЕДИНСТВЕННОЕ место где diffBody. Больше никто не должен.runCompareForGroup()— compareCard. Все ссылки compareBody/compareStatus?llmBtn— compareCard. То же.- SSE handler'ы — ДВА почти идентичных. Дублирование.
showText()—escHtml(classify_raw)ок, ноcounterparty,own_number— НЕ экранированы! XSS.escHtml()— экранирует<>&"'?stepDone/Active/resetStepper— innerHTML через replace. Спецсимволы в id?window._groupsData— устаревает после remove→re-classify. runCompareForGroup использует индекс.showClassifyBtn()— идемпотентно?lucide.createIcons()— не ломает onclick?
Файл 3: contractor/deploy/app_utils.js
Что это: Утилиты: форматирование, removeFile, escHtml, модалки.
Что смотреть:
removeFile()— syncDB+resetStepper+showClassifyBtn через typeof. Если нет — молча.formatDate(),formatSize()— null/undefined safe?escHtml()— все опасные символы:< > & " '?moveUp/Down— не вызывают syncDB (правильно, состав не меняется).
Файл 4: contractor/deploy/services/classify.py
Что это: LLM-классификация. ThreadPoolExecutor, выжимка, вызов LLM.
Что смотреть:
classify_batch()— reset + list_pending. Гонка между ними?_classify_one()—_smart_extractможет упасть до LLM. Обрабатывается?_smart_extract()— неожиданная структура elements_json?_call_llm_classify()— httpxverify=False. MITM уязвимость._safe_json_parse()— edge cases: Null/None, числа без кавычек, пустой ответ.MAX_WORKERS=4— 4 одновременных запроса создают каждый свой пул?LLM_KEYиз env — если не задан?- API key в коде? (берётся из env, ок)
Файл 5: contractor/deploy/services/grouping.py
Что это: Группировка документов по контрактам + виртуальные группы.
Что смотреть:
group_documents()— виртуальные группы: что если parent_number и own_number оба null?- Коллизия normalize_number: разные номера → одинаковый нормализованный.
- Сортировка по doc_date: None у всех → нестабильный порядок.
apply_groups()— нет проверки на существующий contract (дубликат при повторе).supplements_list.remove(s)внутри цикла for — может пропускать элементы.
Файл 6: contractor/deploy/services/process.py
Что это: SSE пайплайн сравнения.
Что смотреть:
run_pipeline()— битая ссылка supplement→document? timeout? retry?call_llm()— таймаут?- SSE события — все ли обрабатываются на фронте?
Файл 7: contractor/deploy/services/parse.py
Что это: Парсинг PDF/DOCX.
Что смотреть:
- Расширение: .PDF uppercase? Без расширения?
- PDF: битый/зашифрованный → исключение?
- DOCX: .doc (OLE) → исключение?
- Таблицы: пустые, объединённые ячейки?
- file_data = None?
Файл 8: contractor/deploy/services/upload.py
Что это: Multipart загрузка через cgi.FieldStorage.
Что смотреть:
- cgi.FieldStorage deprecated. Большие файлы?
- content_length — отрицательное/огромное → rfile.read?
- filename — path traversal (
../../etc/passwd)? - 100MB файл → весь в памяти.
- contract_id без валидации → SQL.
delete_by_documentдо создания нового → исключение = потеря старого без создания нового.- base64 → +33% размер.
Файл 9: contractor/deploy/db/connection.py
Что это: ThreadedConnectionPool.
Что смотреть:
DB_PASSиз env, пустой → ошибка подключения.minconn=1, maxconn=10— достаточно?- getconn/putconn всегда в finally?
_connectionглобальная — сервер не стартует без БД.
Файл 10: contractor/deploy/db/documents.py
Что это: CRUD documents + classify_raw.
Что смотреть:
insert()— все параметры через %s?set_classification()— classify_raw=None → NULL.set_classify_failed()— не чистит старые поля классификации.delete()— без cascade!get()— SELECT *, может вернуть base64 original_bytes.
Файл 11: contractor/deploy/db/supplements.py
Что это: CRUD supplements + cascade.
Что смотреть:
delete_by_document()— spec_current WHERE contract_id удаляет ВСЁ для контракта. Если несколько supplements → остальные теряют spec_current.list_by_contract()— orphan supplements игнорируются.insert()— нет FK проверки.
Файл 12: contractor/deploy/db/contracts.py
Что это: CRUD contracts.
Что смотреть:
insert()— нет уникальности, можно дубликат.delete_orphaned()— вызывается? Где?
Файл 13: contractor/deploy/db/spec_events.py
Что это: Event Sourcing.
Что смотреть:
get_next_seq()—MAX(seq)+1. Гонка! Два потока → одинаковый seq.reset()— необратимо.apply_ops()— валидация структуры?
Файл 14: contractor/deploy/db/spec_current.py
Что это: Текущая спецификация.
Что смотреть:
- Кто обновляет spec_current после удаления spec_events?
get_elements_json()— где используется?
Файл 15: contractor/deploy/db/prompts.py
Что это: CRUD промптов с версионированием.
Что смотреть:
seed_defaults()—if count>0: return— если есть extract но нет classify → classify не создастся._ensure_classify_prompt()— отдельный механизм, почему?save_new_version()— UPDATE+INSERT не атомарно. Гонка.activate()— гонка.delete_prompt()— проверка is_active потом DELETE. Гонка._serialize()— мутирует словарь.
Файл 16: contractor/deploy/llm_prompt.py
Что это: Билдер промптов.
Что смотреть:
build_prompt()— f-string с данными парсинга. Безопасно?_fetch_prompt()— HTTP к Lucee. Таймаут? Fallback при недоступности?build_classify_prompt()— прямой доступ к БД, не через HTTP. Почему?- FALLBACK_* хардкод — дублирование с БД.
Файл 17: contractor/index.cfm
Что это: HTML-оболочка.
Что смотреть:
?v=1.0.175хардкод — менять при каждом обновлении JS.- pipelineStepper — ○⏳✓ через replace. Надёжно?
- XSS через prompt body в textarea/div?
- Z-index конфликты модалок?
- inline onclick — ломаются при перезагрузке JS?
СВОДНАЯ ТАБЛИЦА (выдать после пофайлового разбора)
| # | Файл:строка | Серьёзность | Тип | Описание | Как исправить |
|---|
ТОП-5 (срочно)
5 проблем, которые надо чинить прямо сейчас.