47 lines
3.8 KiB
Markdown
47 lines
3.8 KiB
Markdown
# v0.0.73 — фиксы по код-ревью Соннета (безопасность + явные баги) (2026-08-24)
|
||
|
||
_code-fixes. По ревью `History/sonnet/2026-08-24-code-review-sonnet.md` (промпт `docs/code-review-sonnet.md`)._
|
||
_Критично перепроверены в коде; спорные пункты — отклонены/отложены (см. ниже)._
|
||
|
||
## Исправлено (6 фиксов)
|
||
|
||
1. **SSRF** (`api_bp.py::upload_refs`) — добавлена валидация `url.startswith(VM_UPLOAD_PREFIX)`;
|
||
недоверенный URL пропускается. Константа `VM_UPLOAD_PREFIX = "https://contracts.kube5s.ru/drhider-upload/"`.
|
||
2. **Path traversal / zip slip** (`api_bp.py`) — функция `_safe_name()`: нормализует слэши,
|
||
отбрасывает `..` и абсолютные пути, сохраняя подпапки (`Подпапка/Акт.txt` → как есть,
|
||
`../../evil.pdf` → ""). Применена в `upload` и `upload_refs`.
|
||
Unit-тест 8 кейсов — все OK.
|
||
3. **Слабый SID** (`session.py`) — `uuid.uuid4().hex[:12]` → `uuid.uuid4().hex` (128 бит).
|
||
4. **Серверная ошибка не отображалась** — серверное событие `event: error` (конфликт с встроенным
|
||
EventSource) → `event: proc_error`; добавлен клиентский `addEventListener('proc_error', …)`
|
||
с показом сообщения.
|
||
5. **ZIP-бомба: обход через поддельный `file_size`** (`extractor.py`) — добавлена проверка
|
||
`total_uncompressed > MAX_UNCOMPRESSED` ПОСЛЕ `zf.read()` с `break` (ранний выход).
|
||
6. **Self-XSS через имя файла** (`index.html`) — добавлена `esc()` и применена к `f.name`
|
||
в `rr()` и `procRow()`.
|
||
|
||
## Отклонено (критично к Соннету)
|
||
- **«cleanup(sid) после /download»** — НЕ сделано: ZIP и CSV скачиваются РАЗДЕЛЬНЫМИ запросами;
|
||
удаление сессии после отдачи ZIP сломало бы скачивание CSV. Сессия и так чистится по TTL (30 мин).
|
||
|
||
## Отложено (требуют решения/риск)
|
||
- idx-мисматч при `expand_zips` (фильтр расширений на бэке) — связано с фичей v0.0.69
|
||
(zip без документов «как есть»), требует решения по поведению.
|
||
- Отмена не проверяется в extract-фазе (.doc liberta 120с) — отдельный фикс.
|
||
- LLM-таймаут тихо обнуляет чанк — логирование/статус.
|
||
- Debug-эндпоинт `session_files` — ограничить/закрыть.
|
||
- LLM prompt injection — задокументировать (класс риска, не фикс кода).
|
||
|
||
## Проверка
|
||
- `py_compile` (app.py, api_bp.py, session.py, extractor.py) — OK; `node --check` — OK.
|
||
- `_safe_name` unit-тест — 8/8 OK.
|
||
- `create_app()` стартует (VERSION 0.0.73).
|
||
- Версия 0.0.72 → 0.0.73.
|
||
|
||
## Проверка на проде (редеплой 20:00, v0.0.73)
|
||
- SSRF: `POST /api/upload_refs` с `url=http://169.254.169.254/...` → `{"count":0,"ok":true}` (URL отклонён).
|
||
- Path traversal: `name="../../evil.txt"` → `{"count":0,"ok":true}` (имя отклонено).
|
||
- SID теперь полный 32 hex (128 бит) — виден в ответе.
|
||
- Регресс: папка с подпапкой + кириллицей → «Обработано 2 файлов: 4.3с»,
|
||
в ZIP `Подпапка/Договор_1.md` + `Акт.md` (пути сохранены, `_safe_name` не сломал подпапки).
|