Документирован аудит Codex: HISTORY + ARCHITECTURE (раздел 8 — безопасность и конкуренция)
This commit is contained in:
@@ -188,3 +188,39 @@ SVC_ID = 1 — фиксированный сервис (Болванка)
|
|||||||
- Gunicorn multi-worker → общие данные через файлы + flock
|
- Gunicorn multi-worker → общие данные через файлы + flock
|
||||||
- `/tmp/` теряется при редеплое
|
- `/tmp/` теряется при редеплое
|
||||||
- User-Agent: `Mozilla/5.0` обязателен (DDoS-Guard)
|
- User-Agent: `Mozilla/5.0` обязателен (DDoS-Guard)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Безопасность и конкуренция (аудит GPT-5.3-Codex, 2026-07-31)
|
||||||
|
|
||||||
|
Полный аудит 29 файлов (~6000 строк Python + vanilla JS). Исправлено в v1.2.19-v1.2.20.
|
||||||
|
|
||||||
|
### Защита от XSS (Frontend)
|
||||||
|
|
||||||
|
- **`_esc(s)` в utils.js** — HTML-escape: `&` → `&`, `"` → `"`, `<` → `<`
|
||||||
|
Применяется ко ВСЕМ данным из API перед `innerHTML`.
|
||||||
|
- **params в scenario-list.js** — `_esc(k)+'='+_esc(v)` (было `k+'='+v` без экранирования).
|
||||||
|
- **JS injection в onclick** — имена сценариев с `'` теперь `replace(/'/g, "\\'")` перед
|
||||||
|
вставкой в JS-строку внутри HTML-атрибута.
|
||||||
|
|
||||||
|
### Защита от гонок (Backend)
|
||||||
|
|
||||||
|
- **`_op_results`** — `threading.Lock()` вокруг всех операций чтения/записи/cleanup.
|
||||||
|
`pop(k, None)` вместо `del dict[k]` — безопасно при конкурентном доступе.
|
||||||
|
- **Advisory lock сценариев** — `pg_try_advisory_lock(hashtext('scenario:{clientId}:{stand}'))`
|
||||||
|
атомарно проверяет и захватывает блокировку. `finally: unlock_scenario()` в `run_scenario()`.
|
||||||
|
- **Tracker** — `_atomic_update()`: read→mutate→write под одним `fcntl.flock`.
|
||||||
|
Исключает lost-update между `add()` и `remove()` из разных воркеров gunicorn.
|
||||||
|
|
||||||
|
### Защита от зависания (Frontend polling)
|
||||||
|
|
||||||
|
- **Счётчик ошибок** в `scenarioPollTimer` — после 5 последовательных ошибок:
|
||||||
|
`stopScenarioPoll()` + `busy=false` + сообщение об ошибке.
|
||||||
|
- **Generation token** в `scenario-form.js` — `_renderGen` предотвращает перезапись
|
||||||
|
нового DOM старыми данными от async `loadStepParams()`.
|
||||||
|
|
||||||
|
### Известные ограничения
|
||||||
|
|
||||||
|
- **`_op_results` in-memory на воркер** — не shared между gunicorn-воркерами.
|
||||||
|
При отсутствии stickiness статус может читаться из API fallback вместо кеша.
|
||||||
|
Решение (отложено): Redis или общая таблица в БД для статусов операций.
|
||||||
|
|||||||
@@ -139,3 +139,52 @@
|
|||||||
При анализе FAIL — видеть контекст: «было 3 running Болванки, возможно конфликт ресурсов».
|
При анализе FAIL — видеть контекст: «было 3 running Болванки, возможно конфликт ресурсов».
|
||||||
Хранить в `runs.context_snapshot JSONB` и `scenario_runs.context_snapshot JSONB`.
|
Хранить в `runs.context_snapshot JSONB` и `scenario_runs.context_snapshot JSONB`.
|
||||||
Данные обезличенные, не гигабайты. Отложено до реальной необходимости.
|
Данные обезличенные, не гигабайты. Отложено до реальной необходимости.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Аудит безопасности GPT-5.3-Codex (2026-07-31)
|
||||||
|
|
||||||
|
Проведён полный code review 29 файлов (~6000 строк). Найдено 11 проблем.
|
||||||
|
Результаты зафиксированы в DOCS/ARCHITECTURE.md (раздел 8).
|
||||||
|
|
||||||
|
### КРИТИЧЕСКИЕ (исправлены)
|
||||||
|
|
||||||
|
1. **XSS через params в scenario-list.js:119** — k/v параметров в innerHTML без `_esc`.
|
||||||
|
Stored XSS через БД сценариев. → v1.2.19
|
||||||
|
|
||||||
|
2. **JS injection в onclick** (scenario-list.js:126-128) — `def.name` в `'...'` без JS-escape.
|
||||||
|
`_esc` не экранирует `'` → разрыв строки. → v1.2.19
|
||||||
|
|
||||||
|
3. **Гонка `_op_results`** (api_test.py:264-280) — dict без lock, читается/пишется/чистится
|
||||||
|
из нескольких потоков. → v1.2.20: `threading.Lock()` + `pop(k, None)`
|
||||||
|
|
||||||
|
4. **Неатомарный lock сценариев** (scenario_defs.py + api_scenario_run.py) —
|
||||||
|
`lock_check` (SELECT) и `INSERT RUNNING` разделены. → v1.2.20: `pg_try_advisory_lock`
|
||||||
|
|
||||||
|
### СРЕДНИЕ (исправлены)
|
||||||
|
|
||||||
|
5. **Lost update трекера** (tracker.py) — `_locked_read` + `_locked_write` в разных lock.
|
||||||
|
→ v1.2.19: `_atomic_update()` под одним lock
|
||||||
|
|
||||||
|
6. **Зависание UI поллинга** (scenario-list.js:211) — пустой catch, `busy` не сбрасывается.
|
||||||
|
→ v1.2.19: счётчик ошибок + `stopScenarioPoll` + `busy=false`
|
||||||
|
|
||||||
|
7. **`has_target` не проверяется** (api_scenario_defs.py:58) — вычисляется и игнорируется.
|
||||||
|
→ v1.2.19: явная проверка
|
||||||
|
|
||||||
|
8. **`_ensure_schema` silent** (pool.py:64) — `except Exception: pass`.
|
||||||
|
→ v1.2.20: `traceback.print_exc()`
|
||||||
|
|
||||||
|
### ПОТЕНЦИАЛЬНЫЕ (исправлены)
|
||||||
|
|
||||||
|
9. **Stale async в редакторе** (scenario-form.js) — `loadStepParams` после `renderEditor`
|
||||||
|
может перезаписать новый DOM. → v1.2.20: `_renderGen` generation token
|
||||||
|
|
||||||
|
10. **validate-cfs хрупкий** (terraform.py:250-254) — фильтрация по тексту исключения.
|
||||||
|
→ v1.2.20: явный `except json.JSONDecodeError`
|
||||||
|
|
||||||
|
### НЕ ИСПРАВЛЕНО (архитектурное ограничение)
|
||||||
|
|
||||||
|
11. **In-memory `_op_results` на воркер** — не shared между gunicorn-воркерами.
|
||||||
|
Статус иногда читается из API fallback. Решение: Redis/БД для статусов.
|
||||||
|
Отложено — низкая вероятность проблемы на практике (2 воркера, stickiness).
|
||||||
|
|||||||
Reference in New Issue
Block a user