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

18 KiB
Raw Blame History

name, description
name description
fix-finding Исправление и верификация конкретной 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) без явной команды владельца — верификация идёт на локальном стенде.
  • Не прячь пробелы доказательств: если среда не дала проверить — назови команду и чего не хватает.