docs: код-ревью Соннета — 15 находок (SSRF, path traversal, слабый SID, zip-бомба, idx-мисматч и др.)
This commit is contained in:
@@ -107,6 +107,7 @@
|
||||
- **2026-08-24-folder-select-implemented.md** — v0.0.68: кнопка «Выбрать папку» (webkitdirectory, рекурсивно, относительный путь).
|
||||
- **2026-08-24-folder-zip-not-lost.md** — v0.0.69: архивы из папки без документов не теряются (добавляются как есть); раскрытие zip с документами; склонение счётчика.
|
||||
- **2026-08-24-pull-retry-limit-counter.md** — v0.0.72: ретраи pull из ВМ, корректная ошибка лимита сессии, счётчик дедупа.
|
||||
- **2026-08-24-code-review-sonnet.md** — код-ревью Соннета: 15 багов (SSRF, path traversal, слабый SID, zip-бомба и др.), промпт в docs/code-review-sonnet.md.
|
||||
- **2026-08-24-upload-logic-schema.md** — подробная схема логики загрузки + найденные баги/несоответствия.
|
||||
- **2026-08-24-help-button-limits-text.md** — v0.0.70: заметная кнопка HELP (вместо «?») + пояснение статуса «пропущен» в ограничениях.
|
||||
- **2026-08-24-ui-locks-session-freeze.md** — v0.0.66: блокировки UI (выбор/удаление) на время работы, заморозка сессии + кнопка «Новая сессия».
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Код-ревью DrHider — ответ Соннета (2026-08-24)
|
||||
|
||||
_sonnet. Промпт: `docs/code-review-sonnet.md` (12 файлов кода, 8 вопросов)._
|
||||
_Ревью по v0.0.72. Ничего не исправлено — только задокументировано._
|
||||
|
||||
## 🔴 Критичные
|
||||
|
||||
| Баг | Файл | ~строка | Суть | Фикс |
|
||||
|---|---|---|---|---|
|
||||
| **SSRF** | `api_bp.py` `upload_refs` | ~93 | `url` из клиентского JSON без валидации → `httpx.stream("GET", url)` + `follow_redirects=True`. Доступ к metadata/kube-apiserver | валидировать URL-prefix == `VM_UPLOAD_URL` до запроса |
|
||||
| **Path traversal / zip slip** | `api_bp.py` + `builder.py` | ~88, ~55 | `name = ref.get("name")` без санитизации → `../../evil.md` в выходном ZIP | `name = os.path.basename(ref.get("name",""))` |
|
||||
|
||||
## 🟠 Средние
|
||||
|
||||
| Баг | Файл | ~строка | Суть |
|
||||
|---|---|---|---|
|
||||
| Серверная ошибка не отображается | `api_bp.py` + `index.html` | ~388, ~840 | сервер шлёт `event: error` — это встроенное имя EventSource; клиент не слушает кастомное → «SSE connection failed» вместо реального msg. Переименовать в `proc_error` |
|
||||
| idx-мисматч при `expand_zips` на бэке | `obfuscator.py` + `index.html` | ~90, sendIdx | фронт фильтрует zip по `.pdf/.doc/.docx/.txt/.md`, бэк расширяет ВСЕ файлы → число/порядок не совпадает → `sendIdx[idx]=undefined` |
|
||||
| Слабый SID (48 бит) | `session.py` | 85 | `uuid4().hex[:12]`; убрать `[:12]` → 128 бит |
|
||||
| ZIP-бомба: обход через поддельный `file_size` | `extractor.py` | ~240 | лимит проверяется по `info.file_size` (central dir, подделывается) ДО чтения; проверять ПОСЛЕ `zf.read()` |
|
||||
| Неатомарность pull при ошибке | `api_bp.py` `upload_refs` | ~115 | при исчерпании ретраев 502 БЕЗ `session_id`; частичная сессия-сирота до TTL |
|
||||
|
||||
## 🟡 Мелкие
|
||||
|
||||
| Баг | Файл | ~строка | Суть |
|
||||
|---|---|---|---|
|
||||
| Отмена не работает в extract-фазе | `obfuscator.py` | ~130 | в extract-цикле нет `cancel_event.is_set()`; .doc (liberta 120с) → отмена ждёт все извлечения |
|
||||
| TOCTOU: сессия между check и pause_ttl | `api_bp.py` | ~165-171 | `get_files` (lock снят) → `pause_ttl`; маловероятно |
|
||||
| `fileTimers` не заполняется | `index.html` | ~792 | мёртвый код, fallback всегда 0.0с |
|
||||
| Self-XSS: `f.name` в innerHTML | `index.html` | ~280, ~380 | имя файла без escaping → `textContent`/`escapeHtml()` |
|
||||
| ZIP с non-doc расширениями → мусор | `extractor.py` | ~257 | фильтровать расширения в `expand_zips` |
|
||||
| Сессия не чистится после `/download` | `api_bp.py` | ~430 | 500МБ висит 30 мин → OOM; `cleanup(sid)` |
|
||||
| LLM-таймаут тихо обнуляет чанк | `llm_client.py` + `scanner.py` | ~70, ~220 | `except → return []`, PII не найден без предупреждения |
|
||||
| Debug-эндпоинт `session_files` | `api_bp.py` | ~139 | список файлов любой сессии по SID без токена |
|
||||
| LLM prompt injection | `scanner.py` | ~200 | текст документа дословно в prompt → подавление NER |
|
||||
|
||||
## Подтверждено корректным (Соннет)
|
||||
- Различимость «сессия не найдена» vs «лимит» ✓
|
||||
- Гонок на `cancel_event`/`queue` нет (thread-safe) ✓
|
||||
- Утечек httpx/zipfile/EventSource/таймеров нет ✓
|
||||
- Пустой txt, битый pdf, 0 файлов/все сверх лимита — обрабатываются ✓
|
||||
|
||||
## Что уже исправлено ранее (до этого ревью)
|
||||
- Ретраи pull (v0.0.72) — Соннет отметил остаточную неатомарность.
|
||||
- Разделение статусов «пропущен (лимит)» / «не извлечён» (v0.0.71).
|
||||
- Счётчик дедупа (v0.0.72).
|
||||
|
||||
## TODO-связь
|
||||
- Открытый баг кнопки «Обфусцировать» при 0 файлов — `docs/WhatTODO.md` (не покрыт ревью Соннета).
|
||||
Reference in New Issue
Block a user