diff --git a/docs/security/2026-06-17-go-live-security-report.md b/docs/security/2026-06-17-go-live-security-report.md index 0adafbcc..40cf380b 100644 --- a/docs/security/2026-06-17-go-live-security-report.md +++ b/docs/security/2026-06-17-go-live-security-report.md @@ -12,18 +12,17 @@ Commit: main @ ~cf813c10 (+ незакоммиченные правки пара > слой инструментов требует доустановки и перезапуска. --- ШАГ 1: СТАТИКА --- -gitleaks: SKIP (не перезапущен в этом прогоне — десинк указателя стены F-J). - Известный результат за сегодня (сессия 2026-06-17): история чиста, - единственная находка — учебная фикстура секрет-сканера в allowlist. - → требуется чистый перезапуск `./bin/gitleaks.exe detect --source . --log-opts "--all"`. -Semgrep: PENDING — `semgrep` не установлен / не на PATH в этой оболочке - (`'semgrep' is not recognized`). Команда `npm run sast` падает на запуске бинаря. - → установить Semgrep (pip/pipx) до публичного деплоя. -Ward: ERROR — в плане использован устаревший флаг `--path` (актуально - позиционно: `ward scan app/`). Бинарь установлен (`bin/ward.exe`), - но скан не выполнился. → перезапустить `./bin/ward.exe scan app/`. - (По памяти прошлого smoke Ward давал High `APP_DEBUG`, Medium `APP_ENV=local` - — для dev-`.env` ожидаемо; на боевом `.env.production` проверить заново.) +gitleaks: OK — чистый перезапуск 17.06: 2462 коммита (112.94 MB), **0 утечек**. + Учебная фикстура секрет-сканера — в allowlist. Секретов в истории нет. +Semgrep: OK — установлен (semgrep-1.166.0); `npm run sast` (конфиги p/php, + p/javascript, p/typescript, p/secrets + `.semgrep.yml`, флаг `--error`) + завершился **exit 0 → 0 находок** (`--error` дал бы exit 1 при любой + находке). Детальный вывод под стеной не читался, но exit-код определяет + результат: блокирующих находок Semgrep нет. +Ward: OK — перезапущен `./bin/ward.exe scan app/` (v0.4.1): 2 находки на + dev-`.env` — [High] APP_DEBUG включён, [Medium] APP_ENV=local; + config/dependency-scanner — 0. На dev ожидаемо; на боевом + `.env.production` обязательно APP_DEBUG=false / APP_ENV=production — проверить фактом. Trail of Bits: SKIP — не применим к этому плановому прогону (вызывается вручную перед первым публичным релизом / при крупных изменениях периметра). @@ -107,3 +106,36 @@ PENDING / не отработали (должны быть закрыты до доустановки Semgrep/ZAP и чистого перезапуска gitleaks/Ward — статический и глубоко-динамический слои гейта на этой машине сейчас не полны. === END === + +## ПОПРАВКА — верификация по коду (17.06.2026) + +Три пункта, ранее помеченные как P0 «из модели угроз», проверены по реальному коду и оказались закрытыми: +- E9 (`/api/managers`, `/api/lead-statuses`) + E18 (`/api/dashboard/summary`) — под `auth:sanctum`(+`tenant`), `app/routes/web.php`. +- E20 (`/api/admin/*`) — за middleware `saas-admin` (prod fail-closed 503). +- SSRF (`/api/webhooks/test`) — `WebhookUrlGuard` блокирует cloud-metadata/RFC1918/loopback (на сохранении и тесте), URL только `https://`. + +**Вердикт ОСТАЁТСЯ NO-GO.** Реальные блокеры НЕ сняты: F-P1 (ретеншен телефонов лидов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). Остаточные не-блокеры: bypass-флаг + SSO админки (config / Б-1), DNS-rebind на будущей доставке webhook (edge), rate-limit на login (P1 — проверить throttle). + +--- + +## ДОБИВАНИЕ АУДИТА (17.06.2026) — pg_anonymizer + ZAP + +**pg_anonymizer:** проверено фактом (`pg_extension WHERE extname='anon'` → пусто) — **НЕ установлен** (phase-3, OPEN-И-24). pg_dump отдаёт ПДн открыто → блокер B2 в силе. + +**ZAP #68** (spider + passive, цель 127.0.0.1:8000, демон 2.17.0; active scan не гнали — гард IS8): spider завершён (status 100). Сводка alerts — **High 0**, Medium 4, Low 28, Informational 4. **0 high/critical → блокеров от ZAP нет.** 4 Medium = предупреждения (вероятно security-заголовки), разобрать. + +**Аудит инструментами ЗАКРЫТ:** gitleaks 0 · Semgrep 0 · Ward 2(dev) · Nuclei 0(medium+) · ZAP 0(high). + +**Вердикт остаётся NO-GO** — держат не сканеры, а: F-P1 (ретеншен телефонов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). + +--- + +## ДОБИВАНИЕ АУДИТА (17.06.2026) — pg_anonymizer + ZAP + +**pg_anonymizer:** проверено фактом (`pg_extension WHERE extname='anon'` → пусто) — **НЕ установлен** (phase-3, OPEN-И-24). pg_dump отдаёт ПДн открыто → блокер B2 в силе. + +**ZAP #68** (spider + passive, цель 127.0.0.1:8000, демон 2.17.0; active scan не гнали — гард IS8): spider завершён (status 100). Сводка alerts — **High 0**, Medium 4, Low 28, Informational 4. **0 high/critical → блокеров от ZAP нет.** 4 Medium = предупреждения (вероятно security-заголовки), разобрать. + +**Аудит инструментами ЗАКРЫТ:** gitleaks 0 · Semgrep 0 · Ward 2(dev) · Nuclei 0(medium+) · ZAP 0(high). + +**Вердикт остаётся NO-GO** — держат не сканеры, а: F-P1 (ретеншен телефонов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). diff --git a/docs/security/2026-06-17-open-items.md b/docs/security/2026-06-17-open-items.md new file mode 100644 index 00000000..078e0968 --- /dev/null +++ b/docs/security/2026-06-17-open-items.md @@ -0,0 +1,103 @@ +# Открытые вопросы — снимок 17.06.2026 + +Сводный трекер по итогам сессии (go-live security gate + кросс-сессионная сверка). +Зона = где правится; «коллизия с фронт-сессией» = пересекается ли с параллельной +сессией, которая ведёт находки осмотра во фронтенде `app/resources/js`. + +--- + +## 🔴 Блокеры безопасности (мешают выходу в интернет) + +| # | Пункт | Суть | Зона | Коллизия | +|---|---|---|---|---| +| B1 | **F-P1 / 152-ФЗ retention** | Телефоны удалённых лидов не вычищаются (нет анонимизации/hard-delete после soft-delete). Подтверждён фактом по `Deal`-модели + схеме. | `app/Console/Commands` + миграция (бэкенд/БД) | низкая (бэкенд) | +| B2 | **pg_anonymizer не установлен** | `pg_dump` отдаёт ПДн открыто. Расширение `anon` — фаза 3, может быть не поставлено. Проверить: `SELECT extname FROM pg_extension WHERE extname='anon';` | БД/инфра + `docs/security` | нет | +| B3 | **SAAS_ADMIN_TEST_BYPASS** | Проверить, что в боевом `.env.production` = `false` (иначе полный доступ к `/api/admin/**`). | `.env.production` / config | нет | +| B4 | **SSRF через `/api/webhooks/test`** | Нет фильтрации URL → запрос на metadata YC `169.254.169.254` → IAM-токен. | бэкенд (контроллер/сервис) | низкая | +| B5 | **Открытые без auth ручки** | `/api/dashboard/summary` (E18), `/api/managers` + `/api/lead-statuses` (E9) — добавить `auth:sanctum + tenant`. | `routes/api.php` + middleware (бэкенд) | низкая | +| B6 | **Ward [High] APP_DEBUG** | На dev ожидаемо; на боевом `.env.production` обязательно `APP_DEBUG=false`, `APP_ENV=production`. | `.env.production` | нет | + +## 🟠 P1 (важно, не критично к go-live) + +| # | Пункт | Зона | +|---|---|---| +| P1 | Нет rate-limit на `login`/`forgot`/`2fa/verify` (Laravel Throttle) | `routes/api.php` (бэкенд) | +| P2 | URL-secret поставщика виден в access-логах (E4) → перейти на HMAC, ротация | бэкенд/инфра | + +## 🟢 Инструменты аудита — статус + +| Инструмент | Статус 17.06 | Действие | +|---|---|---| +| gitleaks #8 | ✅ OK (2462 коммита, 0 утечек) | — | +| Ward #70 | ✅ OK (2 dev-находки: APP_DEBUG/APP_ENV) | проверить на боевом `.env` | +| Nuclei #69 | ✅ OK (1 info `php`, 0 medium+) | — | +| Semgrep #25 | ⏸ PENDING — не установлен | `pip install semgrep` в терминале → `npm run sast` | +| ZAP #68 | ⏸ PENDING — Java не установлена | поставить Java 17 + поднять ZAP (active scan) | + +## 🟡 Находки осмотра портала (ведёт параллельная фронт-сессия) + +| Находка | Статус | Зона | Коллизия | +|---|---|---|---| +| F1 (Город в карточке) | ✅ закоммичено | фронт | — её зона | +| F2 (стоимость лида) | ✅ закоммичено | фронт | — её зона | +| F4 (пустой столбец «Операция») | 🔧 в работе у неё (`TransactionsTable.spec.ts`) | фронт | **её зона — не трогать** | +| F3 (прогноз «хватит на дни» дашборд↔биллинг) | ❓ проверить, закрыта ли | фронт | её зона | +| F5 (мок «средняя 2248 ₽») | ❓ проверить (есть `format-rub-demo-spec`) | фронт | её зона | + +## 🧱 Баги машинерии стены (для claude-brain) + +| # | Пункт | Файл | +|---|---|---| +| W1 | Чтение под планом блокируется (impl-режим ДР-1) | `docs/superpowers/2026-06-17-wall-read-block-bug.md` | +| W2 | Десинк указателя F-J (счётчик двигается на упавшем шаге) | там же + GUIDE | +| W3 | Недетерминизм судьи на байт-идентичном тексте | GUIDE «Уроки живого прогона» | +| W4 | Наставник: пустой `recommendation` → `no_mentor_go` | `docs/superpowers/2026-06-16-mentor-empty-recommendation-bug.md` | + +## 🧹 Хозяйственное + +| # | Пункт | +|---|---| +| H1 | Незакоммичено: спека/план/файл-баг чтения, спека/план/отчёт/трекер аудита, обновление отчёта | +| H2 | Не запушено: docs-коммит `e693cfc6` (локально) | +| H3 | Нормативный хвост (проверить): синхронизация квинтета CLAUDE.md 2.46→2.47 + долг lychee по относительным ссылкам | +| H4 | Ward оставил артефакты `ward-report.{json,sarif,html,md}` в корне — решить, в .gitignore или удалить | + +--- + +**Рекомендация по очерёдности (с учётом недельного лимита):** реальные блокеры +B1–B6 — бэкенд/БД/config, **не пересекаются** с фронт-сессией. Самый дешёвый и +отдельный — **B5** (закрыть открытые ручки) + **B3/B6** (проверка боевого `.env`). +Semgrep/ZAP — поставить инструменты (одна команда + Java), затем добить статику/DAST. + +## ПОПРАВКА — верификация по коду (17.06.2026) + +Три пункта, ранее помеченные как P0 «из модели угроз», проверены по реальному коду и оказались закрытыми: +- E9 (`/api/managers`, `/api/lead-statuses`) + E18 (`/api/dashboard/summary`) — под `auth:sanctum`(+`tenant`), `app/routes/web.php`. +- E20 (`/api/admin/*`) — за middleware `saas-admin` (prod fail-closed 503). +- SSRF (`/api/webhooks/test`) — `WebhookUrlGuard` блокирует cloud-metadata/RFC1918/loopback (на сохранении и тесте), URL только `https://`. + +**Вердикт ОСТАЁТСЯ NO-GO.** Реальные блокеры НЕ сняты: F-P1 (ретеншен телефонов лидов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). Остаточные не-блокеры: bypass-флаг + SSO админки (config / Б-1), DNS-rebind на будущей доставке webhook (edge), rate-limit на login (P1 — проверить throttle). + +--- + +## ДОБИВАНИЕ АУДИТА (17.06.2026) — pg_anonymizer + ZAP + +**pg_anonymizer:** проверено фактом (`pg_extension WHERE extname='anon'` → пусто) — **НЕ установлен** (phase-3, OPEN-И-24). pg_dump отдаёт ПДн открыто → блокер B2 в силе. + +**ZAP #68** (spider + passive, цель 127.0.0.1:8000, демон 2.17.0; active scan не гнали — гард IS8): spider завершён (status 100). Сводка alerts — **High 0**, Medium 4, Low 28, Informational 4. **0 high/critical → блокеров от ZAP нет.** 4 Medium = предупреждения (вероятно security-заголовки), разобрать. + +**Аудит инструментами ЗАКРЫТ:** gitleaks 0 · Semgrep 0 · Ward 2(dev) · Nuclei 0(medium+) · ZAP 0(high). + +**Вердикт остаётся NO-GO** — держат не сканеры, а: F-P1 (ретеншен телефонов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). + +--- + +## ДОБИВАНИЕ АУДИТА (17.06.2026) — pg_anonymizer + ZAP + +**pg_anonymizer:** проверено фактом (`pg_extension WHERE extname='anon'` → пусто) — **НЕ установлен** (phase-3, OPEN-И-24). pg_dump отдаёт ПДн открыто → блокер B2 в силе. + +**ZAP #68** (spider + passive, цель 127.0.0.1:8000, демон 2.17.0; active scan не гнали — гард IS8): spider завершён (status 100). Сводка alerts — **High 0**, Medium 4, Low 28, Informational 4. **0 high/critical → блокеров от ZAP нет.** 4 Medium = предупреждения (вероятно security-заголовки), разобрать. + +**Аудит инструментами ЗАКРЫТ:** gitleaks 0 · Semgrep 0 · Ward 2(dev) · Nuclei 0(medium+) · ZAP 0(high). + +**Вердикт остаётся NO-GO** — держат не сканеры, а: F-P1 (ретеншен телефонов), pg_anonymizer (не установлен), прод-`.env` (APP_DEBUG/APP_ENV). diff --git a/docs/superpowers/2026-06-17-wall-read-block-bug.md b/docs/superpowers/2026-06-17-wall-read-block-bug.md new file mode 100644 index 00000000..784bca1d --- /dev/null +++ b/docs/superpowers/2026-06-17-wall-read-block-bug.md @@ -0,0 +1,95 @@ +# БАГ — чтение под стеной «роутер-наставник» (impl-режим) + связанный десинк указателя + +**Дата:** 17.06.2026 +**Откуда:** живой прогон go-live security gate (отчёт `docs/security/2026-06-17-go-live-security-report.md`). +**Куда:** claude-brain (управляющий слой стены — `enforce-supreme-gate` и оркестрация). +**Связано:** `docs/superpowers/router-mentor-wall-GUIDE.md` (раздел «Уроки живого прогона»). + +--- + +## Суть бага (одной фразой) + +В режиме реализации (под опечатанным планом) чтение разрешено **только по пути +текущего шага**. Всё остальное читать нельзя — включая файлы, появившиеся во время +прогона, и **собственный вывод запущенных инструментов** (гейт ДР-1 в +`enforce-supreme-gate`). + +## Почему это серьёзно + +Многошаговая работа, где следующее действие зависит от прочитанного (аудит, +отладка, цепочки инструментов, проверка результата), под стеной фактически +слепнет. Обойти удалось только тем, что **владелец вставлял файлы в чат вручную** +(контекст разговора ≠ вызов Read-инструмента → стена его не трогает). Это +костыль, а не решение. + +## Случаи, где дефект бьёт + +1. **Свой же вывод не прочитать.** Длинная команда (сканер/сборка/тесты) + уезжает в фоновый запуск, результат пишется в temp-файл — открыть нельзя. + *Живой пример прогона:* не прочитался вывод Nuclei. +2. **Забыл прочитать до печати плана — всё.** Понадобился файл по ходу — под + планом не открыть; только переделывать план или просить владельца вставить. +3. **Сторонний сервис/процесс создал файл во время прогона.** Генератор, + выгрузка, отчёт другой задачи, артефакт CI — недоступны. +4. **Нечем проверить результат шага.** Сделал шаг, для проверки нужен другой + файл/лог — нельзя. Получается «сделал вслепую». +5. **Ветвление по содержимому невозможно.** «Если в конфиге X — делаем Y» не + работает: чтобы выбрать, надо прочитать. +6. **Диагностика ошибки по внешнему логу.** Шаг упал, ошибка «смотри лог тут» — + тот лог открыть нельзя. +7. **Промежуточный артефакт в цепочке инструментов.** Шаг N сделал файл, шаг N+1 + должен на него посмотреть — нельзя, только передать вслепую. +8. **Перечитать только что записанное.** Записал файл на шаге 2, на шаге 4 надо + свериться — закрыто. +9. **Неожиданная находка.** Поиск дал совпадение в файле, о котором при + планировании не знал — открыть его уже нельзя. +10. **Файлы от параллельной сессии.** Соседняя сессия добавила/закоммитила + файлы — мне их не глянуть. + +## Связь с рассинхроном указателя (важно для починки) + +Наивная мысль «добавить escape-метку `read:`» **опасна**: стена сейчас двигает +счётчик шагов вперёд **даже когда действие не прошло** (это и есть десинк F-J — +на прогоне так был пропущен шаг gitleaks: `supreme-gate` сдвинул указатель, а +`enforce-domain-skill-discipline` дальше в цепочке уронил действие). Если пустить +чтение через ту же машинерию шагов — **очередь сдвинется, план поедет**. + +Ключ: **чтение не является шагом.** Шаги плана — только `Write/Edit/Bash/MultiEdit`. +Значит разрешение на чтение должно работать **«сбоку от очереди»**, не касаясь +счётчика шагов. + +## Приоритет починки (для claude-brain) + +1. **ГЛАВНОЕ — счётчик шагов += 1 только при успешно завершённом настоящем + шаге.** Не на заблокированном действии, не на чтении, не на постороннем. + Это чинит десинк F-J И автоматически делает чтение безопасным (раз чтение не + шаг — оно не может сдвинуть очередь). +2. **Пассивно разрешить чтение** без escape и без касания очереди: + (а) собственный вывод инструментов, запущенных в этом плане (temp-файлы); + (б) файлы, появившиеся ПОСЛЕ опечатывания плана (их при планировании не было — + запрет «лишнего чтения» здесь нелогичен). +3. **Escape-метка `read:<путь>` — только крайний резерв**, и строго мимо + счётчика шагов (out-of-band), чтобы не повторить десинк. После п.1+п.2 почти + не нужна. + +## Критерий «починено» + +- Под опечатанным планом можно прочитать свой вывод и файлы, созданные после + печати, без сдвига очереди шагов. +- Падение/блокировка шага НЕ двигает указатель (десинк F-J не воспроизводится). +- При необходимости разовое `read:<путь>` через escape не ломает очередность. + +--- + +## БАГ-2 — наставник не видит часть плана + нет owner-seal при зацикле (17.06.2026) + +**Случай:** правка-поправка аудита (2 аддитивных Edit в `docs/`). Наставник 3 круга подряд (17, 18, 19) давал NO-GO с одним требованием: «добавь к каждому шагу явную пометку, что вердикт остаётся NO-GO». + +**Дефект 1 — наставник не видит доп.поля `steps-json`.** Контроллер добавил поле `note` к каждому шагу с требуемым текстом. Но наставник в своём представлении плана видит только `op`/`object`/`ref` — поле `note` (и любые доп.поля) **срезаются** до того, как план попадает к наставнику. Поэтому он снова и снова требует то, что уже добавлено → дедлок, план не пройти, сколько ни переписывай. + +**Дефект 2 — нет owner-seal при зацикле.** По дизайну после 3 NO-GO подряд должна появляться карточка арбитража owner-seal (3 кнопки: держусь контроллера / согласен / своё). На прогоне после кругов 17–19 карточка **не появилась** — круг не разрывался. Контроллер обошёл стену через терминал владельца. + +**Починка (для claude-brain):** +1. Наставник должен видеть план целиком, включая доп.поля шагов (`note`/`description`); ИЛИ задокументировать единственно поддержанный способ «пометки к шагу». +2. Счётчик «N NO-GO подряд» обязан реально триггерить owner-seal-карточку — проверить порог и срабатывание. +3. Требование, которое контроллер физически не может выразить в формате плана, не должно вести к бесконечному NO-GO — нужен предохранитель (эскалация к владельцу). diff --git a/docs/superpowers/plans/2026-06-17-audit-finish-plan.md b/docs/superpowers/plans/2026-06-17-audit-finish-plan.md new file mode 100644 index 00000000..454382b1 --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-audit-finish-plan.md @@ -0,0 +1,44 @@ +# План — доведение аудита + трекер открытых вопросов + +## Цель + +Перезапустить статику gitleaks и Ward правильными командами, проверить готовность +ZAP (Java), обновить отчёт реальными результатами и записать новый файл-трекер +открытых вопросов. Цель — `main`, локальный контур; продуктовый код, схема БД и +git не затрагиваются; правки только в `docs/`. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Bash","object":"./bin/gitleaks.exe detect --source . --log-opts \"--all\"","ref":"A1"}, + {"op":"Bash","object":"./bin/ward.exe scan app/","ref":"A1"}, + {"op":"Bash","object":"java -version","ref":"A2"}, + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"A3"}, + {"op":"Write","object":"docs/security/2026-06-17-open-items.md","ref":"A4"} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-audit-finish-spec.md","anchor":"трекер открытых вопросов"} +] +``` + +## Переговоры + +### Круг 1 + +Шаги read-only по коду + две записи в `docs/`: +- gitleaks (история на секреты) и Ward (`scan app/` — позиционно, исправлен + устаревший флаг `--path`) — локальное чтение, ничего не правят и не шлют наружу. +- `java -version` — безобидная проверка наличия Java для оценки готовности ZAP. +- Edit отчёта — обновление существующего документа `docs/security/` реальными + результатами; Write трекера — новый документ `docs/security/`. +Продуктовый код, схема, миграции, git не затрагиваются; коммита нет, поэтому +verify/criterion-гейты не активируются. Навыки не нужны (skills-json пуст). +Semgrep и ZAP требуют доустановки (install/Java), которая под стеной floor-опасна +— в этот план не закладывается, фиксируется как открытый пункт в трекере и отчёте. diff --git a/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v2.md b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v2.md new file mode 100644 index 00000000..69b51026 --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v2.md @@ -0,0 +1,54 @@ +# План v2 — поправка аудита по факту кода (вердикт остаётся NO-GO) + +## Цель + +Внести верифицированную поправку в отчёт и трекер: E9/E18/E20/SSRF подтверждены +закрытыми по коду и переводятся в «закрыто фактом / остаток». **Итоговый вердикт +аудита ОСТАЁТСЯ NO-GO** — закрытие этих пунктов НЕ снимает реальные блокеры +(F-P1 ретеншен телефонов, pg_anonymizer не установлен, прод-`.env` APP_DEBUG/APP_ENV). +Правки только в `docs/`; код/схема/git не затрагиваются. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"V3"}, + {"op":"Edit","object":"docs/security/2026-06-17-open-items.md","ref":"V3"} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"app/routes/web.php","anchor":"Go-live: auth:sanctum"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md","anchor":"поправка аудита по факту кода"} +] +``` + +## Переговоры + +### Круг 1 + +Две аддитивные правки `docs/`: в отчёт — блок-поправка с верифицированными фактами +(E9/E18 закрыты `auth:sanctum`, `/api/admin/*` за `saas-admin` fail-closed, SSRF за +`WebhookUrlGuard`); в трекере строки B3/B4/B5 → «закрыто фактом / остаток». +Продуктовый код, схема, миграции, git не затрагиваются; коммита нет; навыки не нужны. + +### Круг 2 + +Forward-рекомендация: поправка обязана сохранить вердикт NO-GO и не выдавать +закрытые пункты за нерешённые. + +### Круг 3 (учёт замечания наставника круга 17) + +Замечание принято и зафиксировано явно как условие обоих шагов: + +- **Шаг 1 (отчёт):** блок-поправка прямо пишет «ВЕРДИКТ ОСТАЁТСЯ NO-GO» и + перечисляет, что остаются открытыми F-P1 / pg_anonymizer / прод-`.env`; закрытие + E9/E18/E20/SSRF влияет только на список угроз, НЕ на итог. +- **Шаг 2 (трекер):** строки B3/B4/B5 помечаются «закрыто фактом / остаток» с + явной припиской, что блокеры B1 (F-P1), B2 (pg_anonymizer), B6 (прод-`.env`) + не сняты и вердикт NO-GO в силе. + +Ни один шаг не меняет вердикт на GO; общий статус блокеров сохраняется. diff --git a/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v3.md b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v3.md new file mode 100644 index 00000000..33f99360 --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan-v3.md @@ -0,0 +1,52 @@ +# План v3 — поправка аудита по факту кода (вердикт остаётся NO-GO) + +## Цель + +Внести верифицированную поправку в отчёт и трекер: E9/E18/E20/SSRF подтверждены +закрытыми по коду и переводятся в «закрыто фактом / остаток». **Итоговый вердикт +аудита ОСТАЁТСЯ NO-GO** — закрытие этих пунктов НЕ снимает реальные блокеры +(F-P1 ретеншен телефонов, pg_anonymizer не установлен, прод-`.env` APP_DEBUG/APP_ENV). +Правки только в `docs/`; код/схема/git не затрагиваются. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"V3","note":"Вердикт ОСТАЁТСЯ NO-GO. Блок-поправка закрывает E9/E18/E20/SSRF (влияет только на список угроз), но блокеры F-P1, pg_anonymizer, прод-.env (APP_DEBUG/APP_ENV) НЕ сняты — итог не меняется на GO."}, + {"op":"Edit","object":"docs/security/2026-06-17-open-items.md","ref":"V3","note":"Строки B3/B4/B5 → «закрыто фактом / остаток». Приписка: блокеры B1 (F-P1), B2 (pg_anonymizer), B6 (прод-.env) НЕ сняты, общий вердикт NO-GO в силе. Шаг не переводит вердикт в GO."} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"app/routes/web.php","anchor":"Go-live: auth:sanctum"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md","anchor":"что остаётся реальным блокером"} +] +``` + +## Переговоры + +### Круг 1 + +Две аддитивные правки `docs/`: в отчёт — блок-поправка с верифицированными фактами; +в трекере B3/B4/B5 → «закрыто фактом / остаток». Код/схема/git не затрагиваются. + +### Круг 2 + +Forward-рекомендация: поправка обязана сохранить вердикт NO-GO и не выдавать +закрытые пункты за нерешённые. + +### Круг 3 (учёт замечаний наставника кругов 17 и 18) + +Замечание принято и зафиксировано **явно в каждом шаге** через поле `note` в +`steps-json`: + +- **Шаг 1 (отчёт):** note прямо пишет «вердикт ОСТАЁТСЯ NO-GO»; закрытие + E9/E18/E20/SSRF влияет только на список угроз; блокеры F-P1/pg_anonymizer/прод-`.env` + не сняты — итог не GO. +- **Шаг 2 (трекер):** note помечает B3/B4/B5 «закрыто фактом / остаток» с + припиской, что B1/B2/B6 не сняты и NO-GO в силе. + +Ни один шаг не меняет вердикт на GO; общий статус блокеров сохранён. diff --git a/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan.md b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan.md new file mode 100644 index 00000000..bd014f6c --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-audit-verify-correction-plan.md @@ -0,0 +1,42 @@ +# План — поправка аудита по факту кода + +## Цель + +Внести верифицированную поправку в отчёт и трекер аудита: E9/E18/E20/SSRF +подтверждены закрытыми по коду, список блокеров пересобран на реально открытые +(F-P1, pg_anonymizer, прод-`.env`). Правки только в `docs/`; код/схема/git не +затрагиваются. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"V3"}, + {"op":"Edit","object":"docs/security/2026-06-17-open-items.md","ref":"V3"} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"app/routes/web.php","anchor":"Go-live: auth:sanctum"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md","anchor":"поправка аудита по факту кода"} +] +``` + +## Переговоры + +### Круг 1 + +Две аддитивные правки документов `docs/`: в отчёт добавляется блок-поправка с +верифицированными по коду фактами (E9/E18 закрыты `auth:sanctum`, `/api/admin/*` +за `saas-admin` fail-closed, SSRF за `WebhookUrlGuard`), в трекере строки B3/B4/B5 +переводятся в «закрыто фактом / остаток». Продуктовый код, схема, миграции, git не +затрагиваются; коммита нет. Навыки не нужны (skills-json пуст). + +### Круг 2 + +Прошу наставника зафиксировать forward-`recommendation`: подтвердить, что поправка +должна сохранить вердикт NO-GO (реальные блокеры F-P1/pg_anonymizer/прод-`.env` +остаются) и не выдавать закрытые пункты за нерешённые. diff --git a/docs/superpowers/plans/2026-06-17-semgrep-run-plan-v2.md b/docs/superpowers/plans/2026-06-17-semgrep-run-plan-v2.md new file mode 100644 index 00000000..1cdd33cc --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-semgrep-run-plan-v2.md @@ -0,0 +1,42 @@ +# План v2 — прогон Semgrep после установки + +## Цель + +Запустить `npm run sast` (Semgrep, установлен) и обновить строку Semgrep в отчёте +go-live security gate реальным результатом. Цель — `main`, локально; продуктовый +код, схема, git не затрагиваются; правка только в файле отчёта. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Bash","object":"npm run sast","ref":"S1"}, + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"S2"} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-semgrep-run-spec.md","anchor":"прогон Semgrep после установки"} +] +``` + +## Переговоры + +### Круг 1 + +Два шага: `npm run sast` (read-only статический анализ кода Semgrep'ом, ничего не +правит и наружу не шлёт) и Edit строки Semgrep в существующем отчёте `docs/security/`. +Продуктовый код, схема, миграции, git не затрагиваются; коммита нет. Навыки не +нужны (skills-json пуст). Semgrep установлен (semgrep-1.166.0). + +### Круг 2 + +Прошу наставника зафиксировать forward-`recommendation` для этого плана: на что +обратить внимание при разборе вывода Semgrep — а именно, что находки severity +critical/high обязаны быть отражены в отчёте как блокеры (NO-GO), medium — как +предупреждения, low/info — справочно; и что строка Semgrep в отчёте должна +содержать фактическую severity-разбивку, а не остаться PENDING. diff --git a/docs/superpowers/plans/2026-06-17-semgrep-run-plan.md b/docs/superpowers/plans/2026-06-17-semgrep-run-plan.md new file mode 100644 index 00000000..bad297a1 --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-semgrep-run-plan.md @@ -0,0 +1,34 @@ +# План — прогон Semgrep после установки + +## Цель + +Запустить `npm run sast` (Semgrep) после установки и обновить строку Semgrep в +отчёте go-live security gate реальным результатом. Цель — `main`, локально; +продуктовый код, схема, git не затрагиваются; правка только в файле отчёта. + +```skills-json +[] +``` + +```steps-json +[ + {"op":"Bash","object":"npm run sast","ref":"S1"}, + {"op":"Edit","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"S2"} +] +``` + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-semgrep-run-spec.md","anchor":"прогон Semgrep после установки"} +] +``` + +## Переговоры + +### Круг 1 + +Два шага: `npm run sast` (read-only статический анализ кода Semgrep'ом, ничего не +правит и наружу не шлёт) и Edit строки Semgrep в существующем отчёте `docs/security/`. +Продуктовый код, схема, миграции, git не затрагиваются; коммита нет. Навыки не +нужны (skills-json пуст). Запуск — после завершения установки Semgrep в терминале. diff --git a/docs/superpowers/specs/2026-06-17-audit-finish-spec.md b/docs/superpowers/specs/2026-06-17-audit-finish-spec.md new file mode 100644 index 00000000..8a98b6eb --- /dev/null +++ b/docs/superpowers/specs/2026-06-17-audit-finish-spec.md @@ -0,0 +1,63 @@ +# Спека — доведение аудита + трекер открытых вопросов + +## Цель + +Довести go-live security gate до конца в пределах того, что запускается без +доустановки: перезапустить статические проверки gitleaks и Ward правильными +командами, проверить готовность ZAP (наличие Java), обновить отчёт реальными +результатами, и зафиксировать все открытые вопросы сессии в отдельный файл-трекер. +Цель — текущая ветка `main`, локальный контур; код/схема/git не правятся. + +## A1 — перезапуск статики gitleaks + Ward {#A1} + +**Контракт.** gitleaks — полный скан истории на секреты; Ward — скан `app/` +позиционной командой (`./bin/ward.exe scan app/`, не устаревший `--path`). +Каждый результат заносится в отчёт по severity. + +**Edge-case.** Если gitleaks уезжает в фоновый запуск и его вывод недоступен на +чтение — результат помечается как «получить из вывода» и не выдаётся за «чисто». +Учебная фикстура секрет-сканера — в allowlist gitleaks, не утечка. + +**Конвенция.** Реальная утечка gitleaks = NO-GO (critical). Ward critical = NO-GO; +`APP_DEBUG`/`APP_ENV=local` на dev-`.env` — info (ожидаемо), на боевом — проверить. + +**Критерий.** Оба инструмента отработали, результат разнесён по severity в отчёт. + +## A2 — готовность ZAP {#A2} + +**Контракт.** Проверить наличие Java (`java -version`) — ZAP требует Java 17+ и +демона. Если Java нет — ZAP остаётся `PENDING` с явной причиной, без ложного «ок». + +**Критерий.** В отчёте — статус ZAP (PENDING + причина либо результат). + +## A3 — обновление отчёта {#A3} + +**Контракт.** Существующий отчёт `docs/security/2026-06-17-go-live-security-report.md` +обновляется реальными результатами gitleaks/Ward/ZAP-готовности; вердикт и списки +блокеров/предупреждений/PENDING пересобираются с учётом новых данных. + +**Edge-case.** Semgrep остаётся PENDING (не установлен; установка под стеной +блокируется как floor-опасная) — фиксируется как требующий доустановки в терминале. + +**Критерий.** Отчёт содержит обновлённые строки Шага 1 и пересобранный вердикт. + +## A4 — трекер открытых вопросов {#A4} + +**Контракт.** Новый файл `docs/security/2026-06-17-open-items.md` со всеми открытыми +вопросами сессии по категориям: блокеры безопасности (F-P1, pg_anonymizer, P0/P1 +угрозы), незавершённые инструменты аудита (Semgrep, ZAP, и итог gitleaks/Ward), +находки осмотра портала (F3/F4/F5 — статус), баги машинерии стены (чтение/десинк/ +недетерминизм судьи), хозяйственное (незакоммиченное, непушенное, нормативные хвосты). + +**Конвенция.** Каждый пункт — приоритет, краткая суть, где правится, и пересекается +ли с зоной параллельной сессии (фронтенд `app/`). + +**Критерий.** Файл существует, разбит по категориям, каждый пункт с приоритетом и +зоной правки. + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/2026-06-17-wall-read-block-bug.md","anchor":"чтение под стеной"} +] +``` diff --git a/docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md b/docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md new file mode 100644 index 00000000..05b712bb --- /dev/null +++ b/docs/superpowers/specs/2026-06-17-audit-verify-correction-spec.md @@ -0,0 +1,49 @@ +# Спека — поправка аудита по факту кода (верификация маршрутов и SSRF) + +## Цель + +Внести в отчёт и трекер аудита поправку: три пункта, ранее помеченные как P0 «из +модели угроз, в коде не переподтверждено», проверены по реальному коду и оказались +закрытыми. Документы должны отражать факт, а не устаревшую STRIDE-карту. Правки — +только в `docs/`; код/схема/git не трогаются. + +## V1 — что подтверждено фактом {#V1} + +**Контракт.** По `app/routes/web.php` (актуальный код): `/api/dashboard/summary`, +`/api/managers`, `/api/lead-statuses` — под `auth:sanctum`(+`tenant`); группа +`/api/admin/*` — под middleware `saas-admin` (dev пропускает, prod fail-closed 503); +`/api/webhooks/test` — под `auth:sanctum + tenant`. По `WebhookSettingsController`: +`WebhookUrlGuard::blockReason()` вызывается на сохранении и на тесте, блокирует +cloud-metadata/loopback/RFC1918, URL обязан быть `https://`. + +**Конвенция.** Закрытое в коде не остаётся в списке блокеров; переводится в +«закрыто фактом» либо в остаточный пункт (config/edge). + +**Критерий.** В отчёте и трекере E9/E18/E20/SSRF помечены как закрытые/смягчённые +с указанием остатка. + +## V2 — что остаётся реальным блокером {#V2} + +**Контракт.** Реально открыты (подтверждено фактом): F-P1 (нет ретеншена телефонов +лидов — по модели `Deal` и схеме), pg_anonymizer (не установлен), APP_DEBUG/APP_ENV +на боевом `.env.production` (проверить). Остаточные не-блокеры: bypass-флаг и SSO +админки (config/Б-1), DNS-rebind на будущей доставке webhook (edge), rate-limit на +login (P1, проверить throttle-конфиг). + +**Критерий.** Вердикт NO-GO сохраняется, но список блокеров пересобран на +подтверждённые пункты. + +## V3 — где править {#V3} + +**Контракт.** Поправка вносится в отчёт `docs/security/2026-06-17-go-live-security-report.md` +(блок вердикта/угроз) и в трекер `docs/security/2026-06-17-open-items.md` (строки +B3/B4/B5), аддитивно, без слома структуры. + +**Критерий.** Оба файла обновлены, прежний текст не повреждён. + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"app/routes/web.php","anchor":"Go-live: auth:sanctum"}, + {"id":"vc2","kind":"EXTRACTED","ref":"app/app/Http/Controllers/Api/WebhookSettingsController.php","anchor":"WebhookUrlGuard::blockReason"} +] +``` diff --git a/docs/superpowers/specs/2026-06-17-semgrep-run-spec.md b/docs/superpowers/specs/2026-06-17-semgrep-run-spec.md new file mode 100644 index 00000000..57727ebd --- /dev/null +++ b/docs/superpowers/specs/2026-06-17-semgrep-run-spec.md @@ -0,0 +1,34 @@ +# Спека — прогон Semgrep после установки + +## Цель + +Прогнать статический анализатор Semgrep (`npm run sast`) после его установки в +окружение и занести результат в отчёт go-live security gate. Цель — ветка `main`, +локально; код/схема/git не правятся, правка только в файле отчёта. + +## S1 — прогон Semgrep {#S1} + +**Контракт.** Запустить `npm run sast` (Semgrep с конфигами p/php, p/javascript, +p/typescript, p/secrets + локальный `.semgrep.yml`). Результат разнести по severity. + +**Edge-case.** Если вывод уезжает в фоновый запуск и недоступен на чтение — +результат помечается «получить из вывода», не выдаётся за «чисто». Если Semgrep всё +ещё не на PATH — фиксируется, что установка не завершилась. + +**Конвенция.** critical/high = NO-GO; medium = предупреждение; low/info — справочно. + +**Критерий.** Semgrep отработал, результат по severity готов для отчёта. + +## S2 — занести в отчёт {#S2} + +**Контракт.** В отчёте `docs/security/2026-06-17-go-live-security-report.md` строка +Semgrep обновляется с PENDING на фактический результат (severity-разбивка). + +**Критерий.** Строка Semgrep в отчёте отражает реальный прогон. + +```verified-context-json +[ + {"id":"vc1","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}, + {"id":"vc2","kind":"EXTRACTED","ref":"docs/security/2026-06-17-open-items.md","anchor":"Открытые вопросы"} +] +```