Files
neon-bar86.ru/.agents/skills/fix-finding/SKILL.md
T
2026-10-11 22:24:54 +05:00

219 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: fix-finding
description: Исправление и верификация конкретной security-находки в проекте НЕОН (site/crm/db). Использовать, когда пользователь явно просит исправить, закрыть или залечить уязвимость или finding из аудита — «исправь F-3», «закрой находку из AUDIT/FINDINGS.md», «почини IDOR из аудита и проверь». НЕ использовать для обычных багов, рефакторинга, ревью кода/дизайна, полного аудита (полный аудит делает скилл security-audit — он только документирует) или деплоя.
---
# Fix Finding — минимальный проверенный фикс security-находки в НЕОН
Превратить текущую security-находку в минимальный, проверенный на живом стенде
код-фикс. Если код уже безопасен — доказать это и вернуть `no_change`.
Аудит (поиск и документирование) делает скилл `security-audit`; этот скилл —
вторая стадия над его результатами.
## Источник находки
Найди ID и контекст в одном из мест:
- `AUDIT/FINDINGS.md` — вывод аудит-скилла `security-audit`;
- `АУДИТ-ПЕРЕД-РЕЛИЗОМ.md` — предрелизный аудит, нумерация 1–38
(пункт с пометкой «вопрос к владельцу» — не фикси, это `blocked`);
- F-N — сквозная нумерация security-фиксов в комментариях кода
(`docker-compose.yml`, `crm/server.py`) и в `crm/_security_smoke.mjs`;
- прямое описание от пользователя (файл/эндпоинт + путь атаки).
Если в находке нет файла/эндпоинта — сначала локализуй путь атаки по коду.
Фиксить можно только явно запрошенную находку; соседние находки — доложить,
не чинить.
## Порядок свойств результата
Оценивай результат в этом порядке; более раннее никогда не меняется на более
позднее. «Минимальный» = наименьшее проектно-нативное изменение, закрывающее
все более ранние свойства, а не наименьшее число строк:
1. текущее состояние корректно классифицировано: уязвимо / уже безопасно / недоказано
2. фикс полностью закрывает нарушенную security-границу
3. легитимное поведение и совместимость сохранены
4. проверки проекта (см. гейты ниже) пройдены
5. реализация следует конвенциям проекта
6. патч содержит только необходимый охват
## Карта проекта
- `crm/` — Python 3, stdlib + psycopg, без фреймворков. `server.py` — HTTP +
REST + роли; `notes_api.py` — доски заметок (примешан в Handler);
`storage.py` — PostgreSQL (боевой), `storage_sqlite.py` — dev-двойник
(ограничен: игры/доски/брони/миксы сайта/сброс нумерации → 501);
`js/app.js` + `index.html` + `css/` — панель персонала; `seed/*.json` —
каталог; `migrate/notes_boards.sql` — канонический DDL досок.
- `site/` — React + Vite + Tailwind, собирается в статику под nginx.
`nginx.conf` — edge: лимит `POST /api/orders` (10/мин с IP), real_ip из
X-Forwarded-For, `/media/` отдаёт фото CRM; `src/lib/crm.ts` — мост к API.
- `docker-compose.yml` — site (наружу `127.0.0.1:8080`), crm
(`127.0.0.1:47613`), db (PostgreSQL 17, не публикуется). Секреты только
из `.env`.
- Роли CRM: владелец / администратор / официант / кальянщик — матрица в
README; пять слоёв защиты заказов (nginx-лимит → лимит на гостя →
дедупликация → глобальный тормоз → honeypot).
- Git в проекте НЕТ: перед правкой делай снимок (см. ниже), diff собирай
по снимкам.
Фикс может жить на любом из трёх слоёв (nginx → server.py → storage).
Логика storage продублирована в двух бэкендах: фикс в `storage.py` почти
всегда должен зеркалиться в `storage_sqlite.py` (и наоборот) — аудит не раз
писал «в обоих бэкендах». Учитывай и панель (`crm/js/app.js`): что она
ожидает от изменённого эндпоинта.
## Патч-контракт (до правки)
Изучи саму реализацию, её прямых вызователей, соседние хелперы и разметку
роль×эндпоинт в README. Установи из кода, а не из находки:
- атакующий вход и конкретный путь источник → sink (или сломанный контроль);
- security-инвариант и узкую общую границу, где его enforce;
- легитимное поведение, API-контракты, семантику ошибок, совместимость;
- ближайшие прецеденты в этом же коде (экранирование `esc()` в панели,
параметризованный SQL `%s`, whitelist статики, лимитеры).
Находка — задача о потоке данных и границе, а не о примере входа из отчёта.
Проверь эквивалентные кодировки, alternate-формы парсера, алиасы, всех
вызователей, оба бэкенда и каждую копию security-чувствительного состояния,
которая может обойти фикс. Небезопасное состояние обрабатывай явно — не
принимай, не обрезай и не переинтерпретируй его молча.
## Предпатчевое расследование
До правки запусти один свежий read-only субагент (Agent tool, general-purpose,
с инструкцией не менять файлы). Если делегирование недоступно — сделай тот же
проход сам отдельно, свежим взглядом:
- **Исследователь границы и совместимости:** независимо трассирует путь
источник → sink, находит общую границу enforcement, затронутые входы,
альтернативные представления и состояния, переходы валидация →
использование, конкретные sibling-пути и обходы. Фиксирует легитимные
сценарии и публичное поведение, которые обязаны остаться, и проверяет
вызователей, режимы, ошибки, побочные эффекты, конвенции, готовые хелперы
и применимые команды проверки.
Расследование требует ссылок на файлы/строки проекта и разделения фактов,
выводов и открытых вопросов. По завершении сверь с собственной трассировкой
и выбери границу фикса.
## Снимки вместо git
Перед первым изменением скопируй оригиналы изменяемых файлов в
`AUDIT/FIXES/.snapshots/<ID>/` (создай каталог). По снимкам собери diff для
ревью и отчёта (PowerShell `Compare-Object`, `fc` или `git diff --no-index`,
если git есть на хосте). Не оставляй временных файлов-двойников вида `.bak`
в рабочем дереве после завершения.
## Реализация
1. Протрассируй путь и верни `no_change`, если код уже безопасен — без
спекулятивных правок.
2. Где возможно, прогони через границу минимальный высокосигнальный repro
и один легитимный контрольный запрос по тому же пути (живой стенд ниже).
3. Сделай наименьший фикс на общей границе. Предпочитай соседние хелперы и
установленные API. Не расширяйся на редизайн, уборку и соседние находки.
4. До верификации атакуй свой патч, а не защищай: проверь каждого прямого
вызователя изменённого хелпера и обе ветки каждого изменённого условия.
Найди (а) один sibling-путь, представление или копию, которые всё ещё
доходят до sink, (б) один обычный/дефолтный вход, который патч начал
отвергать или переинтерпретировать. Если хоть одно есть — переделай.
5. Верифицируй по гейтам (порядок обязателен).
## Гейты верификации
1. **Синтаксис/сборка** — только изменённое:
- Python: `python -m py_compile crm\server.py crm\storage.py crm\storage_sqlite.py crm\notes_api.py` (в crm/);
- сайт (если тронут `site/`): из `site/` — `npx tsc --noEmit`, затем `npm run build`;
- движок миксов (если тронут `site/src` миксы/стоп-лист): `npm run test:engine`.
2. **Security-триггер** — repro находки на живом стенде больше не
воспроизводится + один альтернативный класс вредоносного входа.
3. **Легитимный контроль** — обычный сценарий по тому же пути работает;
соседние роли не сломаны (токены owner/admin/waiter — матрица в README).
### Живой стенд
- **Postgres-стек** (нужен для игр, досок, броней, миксов, сброса нумерации —
в sqlite это 501): `docker compose up -d --build`, CRM на
`127.0.0.1:47613`, сайт на `127.0.0.1:8080`.
- **SQLite-стенд** без Docker: `start-crm.bat` (`CRM_DB=sqlite`,
`CRM_ALLOW_DEFAULT_PASSWORDS=1`, порт 47613).
- Сайт-дев: в `site/` `npm run dev`; если docker уже держит 8080 —
`npm run dev -- --port 8090 --strictPort`.
- Перед стартом проверь, кто слушает порт (`netstat -ano | findstr :47613`):
8080/47613 держит docker-стек, **8081 — чужой процесс, его не трогать**.
- Доступы стенда — из `.env` (SMOKE_* нет в .env — задавай при запуске скрипта).
### Регрессионный артефакт
Проект верифицирует фиксы smoke-скриптами по образцу `crm/_security_smoke.mjs`:
`fetch` против `127.0.0.1:47613`, PASS/FAIL, ненулевой exit при провале,
пароли через env (`SMOKE_OWNER_PW`, `SMOKE_WAITER_PW`). Добавь проверки
фикса в `_security_smoke.mjs` или создай сфокусированный `crm/_fix_<ID>.mjs`
по тому же образцу. Помни побочные эффекты: lockout-тест закрывает
`waiter.mark` на 15 минут (порядок проверок важен), тест смены пароля обязан
возвращать пароль обратно.
## Ревью кандидата
После реализации и фокусных проверок запусти одного свежего read-only
субагента (Agent tool; инструкции не редактировать и не делегировать). Дай
ему только находку, корень проекта, авторизованный объём, конвенции и текущий
diff кандидата — без обоснований, отчёта исследователя и заявлений, что
тесты прошли. Если делегирование недоступно — сделай этот проход сам,
отдельно, до финальной верификации:
- **Ревьюер обходов и регрессий:** реконструирует инвариант и ищет конкретный
выживший маршрут через затронутые входы, эквивалентные представления,
границы парсеров, алиасы, оба бэкенда, слой nginx и щели
валидация → использование; трассирует изменённые условия и вызователей на
предмет конкретной поломки легитимных входов, контрактов, ошибок,
побочных эффектов и конвенций.
Находки ревьюера — гипотезы: подтверждай их по коду или фокусным запуском,
правь только подтверждённые в рамках находки и совместимости. Затем
прогони затронутые гейты заново и убедись, что временных и посторонних
изменений не осталось. Только один цикл ревью.
Верни `blocked`, если находка может быть реальной, но не хватает существенных
данных, инструментария, доступа или это продуктовое решение владельца
(пример: №38 «waiter вне смены видит телефоны» из предрелизного аудита) —
тогда безопасный фикс невозможно завершить или проверить.
## Отчёт
Напиши `AUDIT/FIXES/<ID>.md` и проставь в источнике находки (`AUDIT/FINDINGS.md`
или `АУДИТ-ПЕРЕД-РЕЛИЗОМ.md`) статус фикса со ссылкой на отчёт. В финальном
ответе в чате обязательно:
- outcome: `fixed` / `no_change` / `blocked`
- конкретный уязвимый путь, security-инвариант и легитимное поведение, которое
обязано было сохраниться
- выбранная стратегия и почему это самый узкий полный вариант (либо — какое
решение владельца отсутствует при `blocked`)
- изменённые файлы и добавленные тесты/артефакты
- команды по гейтам с результатами pass/fail/unknown
- как показано, что исходная находка больше не воспроизводится
- как показано, что легитимное поведение сохранилось
- оставшиеся пробелы и пропущенные проверки, если есть
## Жёсткие правила
- Не докладывай `fixed`, пока не пройдены все гейты по порядку. Проверку
можно пропустить только с обоснованием по коду; недоступная существенная
проверка = `blocked` и явное указание, чего не хватило.
- Не полагайся только на чтение кода, когда возможен фокусный repro на стенде.
- Не расширяй патч на уборку, соседние находки и редизайн без доказательства,
что без этого граница не закрывается.
- Не удаляй и не затирай правки пользователя — git нет, снимки единственная
страховка; не трогай `crm/data/`, `crm/media/`, `.env` и `site/dist`.
- Не ослабляй авторизацию, ролевую матрицу, пять слоёв защиты заказов,
валидацию входа, fail-fast на дефолтных паролях и лимитеры ради зелёных
проверок.
- Не деплой на прод (`docker compose up -d --build site crm`) без явной
команды владельца — верификация идёт на локальном стенде.
- Не прячь пробелы доказательств: если среда не дала проверить — назови
команду и чего не хватает.