docs(security): go-live security gate отчёт 17.06 + уроки прогона в wall-guide
Прогон security-go-live на main, локальная цель 127.0.0.1:8000 — вердикт NO-GO. Блокеры: pg_anonymizer не установлен (ПДн в дампах), F-P1 (телефоны лидов не вычищаются по сроку), P0 из STRIDE (SAAS_ADMIN_TEST_BYPASS / SSRF webhooks-test / открытые ручки). Nuclei чисто (1 info php). Semgrep/ZAP — PENDING. Гайд стены: новый раздел уроков — читать контекст до печати плана, запасной канал вставки в чат, недетерминизм судьи и рассинхрон указателя F-J. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
=== SECURITY GO-LIVE REPORT ===
|
||||
Дата: 2026-06-17
|
||||
Версия схемы: v8.40
|
||||
Commit: main @ ~cf813c10 (+ незакоммиченные правки параллельной сессии F1/F2 в рабочем дереве)
|
||||
Цель: http://127.0.0.1:8000 (локальная копия, гард IS8)
|
||||
|
||||
> NB по процедуре: прогон выполнен под стеной «роутер-наставник». Импликации
|
||||
> отражены ниже — часть статических инструментов не отработала по техническим
|
||||
> причинам (установка/устаревшая команда/десинк указателя), а чтения для анализа
|
||||
> ПДн/угроз получены через вставку файлов в чат (impl-режим стены блокирует Read).
|
||||
> Это **частичный** прогон: динамика и анализ ПДн/угроз — выполнены; статический
|
||||
> слой инструментов требует доустановки и перезапуска.
|
||||
|
||||
--- ШАГ 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` проверить заново.)
|
||||
Trail of Bits: SKIP — не применим к этому плановому прогону (вызывается вручную
|
||||
перед первым публичным релизом / при крупных изменениях периметра).
|
||||
|
||||
--- ШАГ 2: ПДн / 152-ФЗ (анализ по чек-листу + код/схема) ---
|
||||
Режим 1 (технический):
|
||||
✅ RLS на `deals`/`users`/`auth_log`/`import_log`/`pd_processing_log` — включён
|
||||
(schema v8.40, политики tenant_isolation). `supplier_leads`/`pd_subject_requests`
|
||||
— SaaS-уровень без RLS осознанно (доступ через BYPASSRLS-воркер/админа).
|
||||
✅ `users.totp_secret` — шифруется `Crypt::encrypt` (не plain).
|
||||
✅ `PdAuditLogger->record(...)` пишет `pd_processing_log` при просмотре/создании
|
||||
сделки (DealController::show/store) — журнал обработки работает на этих путях.
|
||||
✅ DealController телефоны/ПДн в логи НЕ пишет; `activity_log.context` для
|
||||
статуса/назначения — только id/slug.
|
||||
⚠️ MEDIUM — логи: `config/logging.php` `LOG_LEVEL=debug`, PII-scrubbing
|
||||
процессора в каналах нет. Риск утечки ПДн только если телефон попадёт в
|
||||
текст исключения; Sentry-scrubbing (OPEN-И-16) — в `config/sentry.php`,
|
||||
проверить регекс-маску фактически.
|
||||
❌ HIGH — `pg_anonymizer` (OPEN-И-24) ставится только в фазе 3; на момент
|
||||
прогона расширение может быть НЕ установлено → `pg_dump` отдаёт ПДн в
|
||||
открытом виде. Проверить: `SELECT extname FROM pg_extension WHERE extname='anon';`
|
||||
Режим 2 (соответствие):
|
||||
✅ Хранение в РФ (Yandex Cloud ru-central1), `tenant_consents`, `pd_subject_requests`
|
||||
с дедлайном 30 дней (триггер), `processing_restricted` (ст.21 ч.5),
|
||||
append-only hash chain на `pd_processing_log` — присутствуют в схеме.
|
||||
⚠️ Проверить вручную (вне кода): уведомление РКН, реестр обработки (ст.22.1),
|
||||
договоры поручения с crm.bp-gr.ru / Unisender Go / JivoSite, локация
|
||||
серверов Unisender Go.
|
||||
|
||||
--- ШАГ 3: УГРОЗЫ (STRIDE-артефакт, в коде этого прогона не переподтверждено) ---
|
||||
Из модели угроз портала — незакрытые ТОП-приоритеты (P0/P1):
|
||||
❌ P0 — `SAAS_ADMIN_TEST_BYPASS=true` в prod = полный доступ к /api/admin/** (E20/E21).
|
||||
❌ P0 — SSRF через `POST /api/webhooks/test` (E13): нет фильтрации URL →
|
||||
metadata YC 169.254.169.254 → IAM-токен.
|
||||
❌ P0 — `GET /api/dashboard/summary` без auth (E18, MVP-заглушка).
|
||||
❌ P0 — `GET /api/managers`, `GET /api/lead-statuses` без auth (E9) — раскрытие ФИО менеджеров.
|
||||
⚠️ P1 — нет rate-limit на `login`/`forgot`/`2fa/verify` (E1–E3); URL-secret
|
||||
поставщика в access-логах (E4).
|
||||
Baseline защиты (есть): RLS (40 политик), Sanctum SPA auth, per-token rate-limit
|
||||
на webhook тенанта (prod), IP-allowlist на webhook поставщика, HMAC X-Webhook-Signature
|
||||
(prod), auth_log/saas_admin_audit_log, bcrypt, signed URL отчётов.
|
||||
|
||||
--- ШАГ 4: ДИНАМИКА (локальная цель 127.0.0.1:8000) ---
|
||||
Nuclei: OK — 916 шаблонов, 1 match severity INFO (`tech-detect:php`), 0 medium/high/critical.
|
||||
Скан завершён за 7 мин. Info-детект стека — не блокер.
|
||||
ZAP: PENDING — требует Java 17 + запущенного демона; не запускался этим прогоном.
|
||||
→ установить/поднять ZAP до публичного деплоя (глубокий DAST/active scan).
|
||||
|
||||
--- ИЗВЕСТНЫЕ ДОЛГИ (явная проверка) ---
|
||||
F-T2 (/api/admin/* без app-гейта): ЧАСТИЧНО ЗАКРЫТ фактом — по STRIDE-карте
|
||||
группа `/api/admin/**` стоит за middleware `saas-admin` (fail-closed 503 в
|
||||
prod). Остаточный риск переехал в P0 выше: `SAAS_ADMIN_TEST_BYPASS` + SSO
|
||||
(Yandex 360) не реализован до Б-1. → подтвердить в `routes/api.php` +
|
||||
`.env.production` (флаг false), затем снять.
|
||||
F-P1 (152-ФЗ: deals.phones не вычищаются): ОТКРЫТ, подтверждён фактом — в модели
|
||||
`Deal` только `SoftDeletes`, анонимизации/ретеншена телефонов нет; в схеме
|
||||
у `deals.deleted_at` — просто soft-delete (у `users.deleted_at` комментарий
|
||||
«soft delete + анонимизация», у `deals` — нет). `deals.phone`/`phones`/
|
||||
`contact_name` после soft-delete остаются в открытом виде бессрочно.
|
||||
→ реализовать retention-cron (hard-delete или анонимизация phone/contact_name
|
||||
через N дней после deleted_at) + исполнение `request_type='deletion'`.
|
||||
|
||||
=== ВЕРДИКТ: NO-GO ❌ ===
|
||||
Блокеры (critical/high):
|
||||
- ПДн дампы: pg_anonymizer не установлен (HIGH) → pg_dump отдаёт ПДн открыто.
|
||||
- F-P1 (HIGH, 152-ФЗ): нет ретеншена/анонимизации телефонов лидов после soft-delete.
|
||||
- STRIDE P0 (из модели угроз, подтвердить в коде): SAAS_ADMIN_TEST_BYPASS в prod;
|
||||
SSRF в /api/webhooks/test; открытые без auth /api/dashboard/summary, /api/managers,
|
||||
/api/lead-statuses.
|
||||
|
||||
Предупреждения (medium):
|
||||
- Логи без PII-scrubbing процессора (LOG_LEVEL=debug); проверить Sentry-маску фактически.
|
||||
- Нет rate-limit на login/forgot/2fa/verify; URL-secret поставщика в access-логах.
|
||||
|
||||
PENDING / не отработали (должны быть закрыты до публичного деплоя):
|
||||
- Semgrep #25: не установлен (нет на PATH) — установить и перезапустить `npm run sast`.
|
||||
- Ward #70: устаревший флаг в плане — перезапустить `./bin/ward.exe scan app/`.
|
||||
- gitleaks #8: перезапустить чисто (в этом прогоне пропущен из-за десинка стены).
|
||||
- ZAP #68: требует Java-демона — установить и прогнать active scan.
|
||||
|
||||
Условный статус инструментов: даже при закрытии находок вердикт остаётся NO-GO до
|
||||
доустановки Semgrep/ZAP и чистого перезапуска gitleaks/Ward — статический и
|
||||
глубоко-динамический слои гейта на этой машине сейчас не полны.
|
||||
=== END ===
|
||||
@@ -0,0 +1,70 @@
|
||||
# План v2 — Go-Live Security Gate (пред-прод прогон безопасности)
|
||||
|
||||
## Цель
|
||||
|
||||
Прогнать пять шагов гейта безопасности по спеке (статика → ПДн/152-ФЗ → угрозы →
|
||||
динамика → вердикт) на текущей ветке `main`, цель — локальная копия
|
||||
`127.0.0.1:8000`, и собрать письменный отчёт с вердиктом GO / NO-GO. Прогон не
|
||||
правит продуктовый код, схему и не коммитит; единственная запись — файл отчёта.
|
||||
|
||||
```skills-json
|
||||
["security-go-live", "pdn-152fz-audit", "threat-model"]
|
||||
```
|
||||
|
||||
```steps-json
|
||||
[
|
||||
{"op":"Bash","object":"./bin/gitleaks.exe detect --source . --log-opts \"--all\"","ref":"D1"},
|
||||
{"op":"Bash","object":"npm run sast","ref":"D1"},
|
||||
{"op":"Bash","object":"./bin/ward.exe scan --path app/","ref":"D1"},
|
||||
{"op":"Bash","object":"php app/artisan serve --host 127.0.0.1 --port 8000 &","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -tags tech -rate-limit 20 -c 5 -timeout 5 -duc","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -rate-limit 20 -c 5 -timeout 5 -duc -severity medium,high,critical","ref":"D4"},
|
||||
{"op":"Write","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"D5"}
|
||||
]
|
||||
```
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":".claude/skills/security-go-live/references/gate.md","anchor":"IS8 — цель по умолчанию локальная"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/Tooling_v8_3.md","anchor":"security-go-live — go-live security-gate"}
|
||||
]
|
||||
```
|
||||
|
||||
## Переговоры
|
||||
|
||||
### Круг 1
|
||||
|
||||
Доводы по составу шагов:
|
||||
|
||||
- **Статика (gitleaks/Semgrep/Ward)** — read-only по локальному репозиторию и
|
||||
`app/`, ничего не правит и не отправляет наружу. gitleaks читает git-историю,
|
||||
Semgrep (`npm run sast`) и Ward (`./bin/ward.exe scan`) читают код и конфиг.
|
||||
Это безопасные локальные проверки.
|
||||
- **ПДн/152-ФЗ и угрозы** вынесены в навыки `pdn-152fz-audit` и `threat-model`
|
||||
(объявлены в skills-json) — они анализируют схему и код только чтением; их
|
||||
находки попадают в отчёт.
|
||||
- **Динамика (Nuclei)** направлена строго на локальную копию `http://127.0.0.1:8000`
|
||||
(гард IS8 — боевой контур не трогается). Темп ограничен `-rate-limit 20 -c 5`
|
||||
под однопоточный `php artisan serve`, чтобы не перегрузить dev-сервер. Цель —
|
||||
`127.0.0.1`, не `localhost` (резолвер Nuclei на этой машине пропускает `localhost`).
|
||||
Сначала быстрый smoke по тегу `tech`, затем полный прогон severity medium+.
|
||||
- **`php artisan serve`** нужен, чтобы портал был доступен сканеру; запускается
|
||||
фоном на локальном адресе, продуктовых данных и схемы не меняет.
|
||||
- **ZAP (#68)** требует Java-демона и при его отсутствии даёт `PENDING` — поэтому
|
||||
отдельным Bash-шагом не закладывается, его статус (`PENDING`/результат) фиксируется
|
||||
в отчёте шагом 7.
|
||||
- **Отчёт (шаг 7)** — единственная запись прогона: новый файл в `docs/security/`.
|
||||
Код, схема, миграции и git не трогаются; verify/criterion-гейты коммита не
|
||||
затрагиваются, так как коммита нет.
|
||||
|
||||
Известные долги F-T2 (`/api/admin/*` без app-гейта) и F-P1 (152-ФЗ:
|
||||
`deals.phones[]` не вычищаются) проверяются фактом в рамках шагов ПДн и угроз и
|
||||
явно отражаются в вердикте.
|
||||
|
||||
### Круг 2
|
||||
|
||||
Принято возражение наставника по шагу 4: синхронный `php artisan serve` заблокировал
|
||||
бы поток и не дал бы выполнить шаги Nuclei. Исправлено — шаг 4 теперь запускает
|
||||
сервер **фоном** (`php app/artisan serve --host 127.0.0.1 --port 8000 &`), сервер
|
||||
работает параллельно с последующими шагами сканирования. Остальные шаги наставник
|
||||
в круге 2 одобрил поимённо (пп. 1–3, 5–7 — OK); изменён только шаг 4.
|
||||
@@ -0,0 +1,76 @@
|
||||
# План v3 — Go-Live Security Gate (пред-прод прогон безопасности)
|
||||
|
||||
## Цель
|
||||
|
||||
Прогнать пять шагов гейта безопасности по спеке (статика → ПДн/152-ФЗ → угрозы →
|
||||
динамика → вердикт) на текущей ветке `main`, цель — локальная копия
|
||||
`127.0.0.1:8000`, и собрать письменный отчёт с вердиктом GO / NO-GO. Прогон не
|
||||
правит продуктовый код, схему и не коммитит; единственная запись — файл отчёта.
|
||||
|
||||
```skills-json
|
||||
["security-go-live", "pdn-152fz-audit", "threat-model"]
|
||||
```
|
||||
|
||||
```steps-json
|
||||
[
|
||||
{"op":"Bash","object":"./bin/gitleaks.exe detect --source . --log-opts \"--all\"","ref":"D1"},
|
||||
{"op":"Bash","object":"npm run sast","ref":"D1"},
|
||||
{"op":"Bash","object":"./bin/ward.exe scan --path app/","ref":"D1"},
|
||||
{"op":"Bash","object":"php app/artisan serve --host 127.0.0.1 --port 8000 &","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -tags tech -rate-limit 20 -c 5 -timeout 5 -duc","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -rate-limit 20 -c 5 -timeout 5 -duc -severity medium,high,critical","ref":"D4"},
|
||||
{"op":"Write","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"D5"}
|
||||
]
|
||||
```
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":".claude/skills/security-go-live/references/gate.md","anchor":"IS8 — цель по умолчанию локальная"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/Tooling_v8_3.md","anchor":"security-go-live — go-live security-gate"}
|
||||
]
|
||||
```
|
||||
|
||||
## Переговоры
|
||||
|
||||
### Круг 1
|
||||
|
||||
Доводы по составу шагов:
|
||||
|
||||
- **Статика (gitleaks/Semgrep/Ward)** — read-only по локальному репозиторию и
|
||||
`app/`, ничего не правит и не отправляет наружу. gitleaks читает git-историю,
|
||||
Semgrep (`npm run sast`) и Ward (`./bin/ward.exe scan`) читают код и конфиг.
|
||||
Это безопасные локальные проверки.
|
||||
- **ПДн/152-ФЗ и угрозы** вынесены в навыки `pdn-152fz-audit` и `threat-model`
|
||||
(объявлены в skills-json) — они анализируют схему и код только чтением; их
|
||||
находки попадают в отчёт.
|
||||
- **Динамика (Nuclei)** направлена строго на локальную копию `http://127.0.0.1:8000`
|
||||
(гард IS8 — боевой контур не трогается). Темп ограничен `-rate-limit 20 -c 5`
|
||||
под однопоточный `php artisan serve`, чтобы не перегрузить dev-сервер. Цель —
|
||||
`127.0.0.1`, не `localhost` (резолвер Nuclei на этой машине пропускает `localhost`).
|
||||
Сначала быстрый smoke по тегу `tech`, затем полный прогон severity medium+.
|
||||
- **`php artisan serve`** нужен, чтобы портал был доступен сканеру; запускается
|
||||
фоном на локальном адресе, продуктовых данных и схемы не меняет.
|
||||
- **ZAP (#68)** требует Java-демона и при его отсутствии даёт `PENDING` — поэтому
|
||||
отдельным Bash-шагом не закладывается, его статус (`PENDING`/результат) фиксируется
|
||||
в отчёте шагом 7.
|
||||
- **Отчёт (шаг 7)** — единственная запись прогона: новый файл в `docs/security/`.
|
||||
Код, схема, миграции и git не трогаются; verify/criterion-гейты коммита не
|
||||
затрагиваются, так как коммита нет.
|
||||
|
||||
Известные долги F-T2 (`/api/admin/*` без app-гейта) и F-P1 (152-ФЗ:
|
||||
`deals.phones[]` не вычищаются) проверяются фактом в рамках шагов ПДн и угроз и
|
||||
явно отражаются в вердикте.
|
||||
|
||||
### Круг 2
|
||||
|
||||
Принято возражение наставника по шагу 4: синхронный `php artisan serve` заблокировал
|
||||
бы поток и не дал бы выполнить шаги Nuclei. Исправлено — шаг 4 теперь запускает
|
||||
сервер **фоном** (`php app/artisan serve --host 127.0.0.1 --port 8000 &`), сервер
|
||||
работает параллельно с последующими шагами сканирования. Остальные шаги наставник
|
||||
в круге 2 одобрил поимённо (пп. 1–3, 5–7 — OK); изменён только шаг 4.
|
||||
|
||||
### Круг 3
|
||||
|
||||
Навыки `security-go-live`, `pdn-152fz-audit` и `threat-model` уже вызваны; их
|
||||
находки (ПДн Режим 1/2, STRIDE-угрозы, статус известных долгов F-T2/F-P1) войдут в
|
||||
итоговый отчёт шагом 7. Состав и порядок шагов неизменны относительно круга 2.
|
||||
@@ -0,0 +1,81 @@
|
||||
# План v4 — Go-Live Security Gate (пред-прод прогон безопасности)
|
||||
|
||||
## Цель
|
||||
|
||||
Собрать единый письменный отчёт безопасности портала Лидерра с вердиктом
|
||||
**GO / NO-GO** на текущей ветке `main`, цель — локальная копия `127.0.0.1:8000`.
|
||||
Гейт концептуально состоит из пяти проверок, каждая со своим механизмом исполнения:
|
||||
|
||||
- **статика** — Bash-шаги gitleaks, Semgrep (`npm run sast`), Ward;
|
||||
- **ПДн / 152-ФЗ** — объявленный навык `pdn-152fz-audit` (Режим 1 и 2);
|
||||
- **угрозы STRIDE** — объявленный навык `threat-model`;
|
||||
- **динамика** — Bash-шаги: запуск локального сервера и Nuclei по `127.0.0.1`;
|
||||
- **сводный вердикт** — шаг записи отчёта (последний), консолидирующий находки
|
||||
Bash-инструментов и навыков в один файл с GO/NO-GO.
|
||||
|
||||
Прогон не правит продуктовый код, схему и не коммитит; единственная запись —
|
||||
файл отчёта в `docs/security/`.
|
||||
|
||||
```skills-json
|
||||
["security-go-live", "pdn-152fz-audit", "threat-model"]
|
||||
```
|
||||
|
||||
```steps-json
|
||||
[
|
||||
{"op":"Bash","object":"./bin/gitleaks.exe detect --source . --log-opts \"--all\"","ref":"D1"},
|
||||
{"op":"Bash","object":"npm run sast","ref":"D1"},
|
||||
{"op":"Bash","object":"./bin/ward.exe scan --path app/","ref":"D1"},
|
||||
{"op":"Bash","object":"php app/artisan serve --host 127.0.0.1 --port 8000 &","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -tags tech -rate-limit 20 -c 5 -timeout 5 -duc","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -rate-limit 20 -c 5 -timeout 5 -duc -severity medium,high,critical","ref":"D4"},
|
||||
{"op":"Write","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"D5"}
|
||||
]
|
||||
```
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":".claude/skills/security-go-live/references/gate.md","anchor":"IS8 — цель по умолчанию локальная"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/Tooling_v8_3.md","anchor":"security-go-live — go-live security-gate"}
|
||||
]
|
||||
```
|
||||
|
||||
## Переговоры
|
||||
|
||||
### Круг 1
|
||||
|
||||
Доводы по составу шагов:
|
||||
|
||||
- **Статика (gitleaks/Semgrep/Ward)** — read-only по локальному репозиторию и
|
||||
`app/`, ничего не правит и не отправляет наружу. gitleaks читает git-историю,
|
||||
Semgrep (`npm run sast`) и Ward (`./bin/ward.exe scan`) читают код и конфиг.
|
||||
- **ПДн/152-ФЗ и угрозы** выполняются навыками `pdn-152fz-audit` и `threat-model`
|
||||
(объявлены в skills-json) — они анализируют схему и код только чтением; их
|
||||
находки попадают в отчёт шагом 7. Эти две проверки — не Bash-шаги, а навыки.
|
||||
- **Динамика (Nuclei)** направлена строго на локальную копию `http://127.0.0.1:8000`
|
||||
(гард IS8). Темп ограничен `-rate-limit 20 -c 5` под однопоточный
|
||||
`php artisan serve`. Цель — `127.0.0.1`, не `localhost` (резолвер Nuclei
|
||||
пропускает `localhost`). Сначала smoke по тегу `tech`, затем полный medium+.
|
||||
- **`php artisan serve`** запускается фоном на локальном адресе, продуктовых данных
|
||||
и схемы не меняет.
|
||||
- **ZAP (#68)** требует Java-демона; при его отсутствии — `PENDING`, статус
|
||||
фиксируется в отчёте шагом 7 (отдельным Bash-шагом не закладывается).
|
||||
- **Отчёт (шаг 7)** — единственная запись прогона: новый файл в `docs/security/`.
|
||||
Код, схема, миграции и git не трогаются; коммита нет.
|
||||
|
||||
Известные долги F-T2 (`/api/admin/*` без app-гейта) и F-P1 (152-ФЗ:
|
||||
`deals.phones[]` не вычищаются) проверяются фактом и явно отражаются в вердикте.
|
||||
|
||||
### Круг 2
|
||||
|
||||
Возражение по синхронному `php artisan serve` принято: шаг 4 запускает сервер
|
||||
**фоном** (`… &`), он работает параллельно с шагами Nuclei. Остальные шаги
|
||||
одобрены поимённо (пп. 1–3, 5–7 — OK).
|
||||
|
||||
### Круг 3
|
||||
|
||||
Пять концептуальных проверок гейта сопоставлены с механизмом исполнения прямо в
|
||||
`## Цель`: статика и динамика — Bash-шаги steps-json; ПДн и угрозы — объявленные
|
||||
навыки skills-json (не Bash-шаги); сводный вердикт — шаг 7 записи отчёта. Семь
|
||||
строк steps-json покрывают проверки «статика» (3), «динамика» (3) и «вердикт» (1);
|
||||
проверки «ПДн» и «угрозы» исполняются навыками. Несостыковки между числом
|
||||
концептуальных проверок и числом Bash-шагов нет — это разные механизмы.
|
||||
@@ -0,0 +1,62 @@
|
||||
# План — Go-Live Security Gate (пред-прод прогон безопасности)
|
||||
|
||||
## Цель
|
||||
|
||||
Прогнать пять шагов гейта безопасности по спеке (статика → ПДн/152-ФЗ → угрозы →
|
||||
динамика → вердикт) на текущей ветке `main`, цель — локальная копия
|
||||
`127.0.0.1:8000`, и собрать письменный отчёт с вердиктом GO / NO-GO. Прогон не
|
||||
правит продуктовый код, схему и не коммитит; единственная запись — файл отчёта.
|
||||
|
||||
```skills-json
|
||||
["security-go-live", "pdn-152fz-audit", "threat-model"]
|
||||
```
|
||||
|
||||
```steps-json
|
||||
[
|
||||
{"op":"Bash","object":"./bin/gitleaks.exe detect --source . --log-opts \"--all\"","ref":"D1"},
|
||||
{"op":"Bash","object":"npm run sast","ref":"D1"},
|
||||
{"op":"Bash","object":"./bin/ward.exe scan --path app/","ref":"D1"},
|
||||
{"op":"Bash","object":"php app/artisan serve --host 127.0.0.1 --port 8000","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -tags tech -rate-limit 20 -c 5 -timeout 5 -duc","ref":"D4"},
|
||||
{"op":"Bash","object":"bin/nuclei.exe -u http://127.0.0.1:8000 -rate-limit 20 -c 5 -timeout 5 -duc -severity medium,high,critical","ref":"D4"},
|
||||
{"op":"Write","object":"docs/security/2026-06-17-go-live-security-report.md","ref":"D5"}
|
||||
]
|
||||
```
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":".claude/skills/security-go-live/references/gate.md","anchor":"IS8 — цель по умолчанию локальная"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/Tooling_v8_3.md","anchor":"security-go-live — go-live security-gate"}
|
||||
]
|
||||
```
|
||||
|
||||
## Переговоры
|
||||
|
||||
### Круг 1
|
||||
|
||||
Доводы по составу шагов:
|
||||
|
||||
- **Статика (gitleaks/Semgrep/Ward)** — read-only по локальному репозиторию и
|
||||
`app/`, ничего не правит и не отправляет наружу. gitleaks читает git-историю,
|
||||
Semgrep (`npm run sast`) и Ward (`./bin/ward.exe scan`) читают код и конфиг.
|
||||
Это безопасные локальные проверки.
|
||||
- **ПДн/152-ФЗ и угрозы** вынесены в навыки `pdn-152fz-audit` и `threat-model`
|
||||
(объявлены в skills-json) — они анализируют схему и код только чтением; их
|
||||
находки попадают в отчёт.
|
||||
- **Динамика (Nuclei)** направлена строго на локальную копию `http://127.0.0.1:8000`
|
||||
(гард IS8 — боевой контур не трогается). Темп ограничен `-rate-limit 20 -c 5`
|
||||
под однопоточный `php artisan serve`, чтобы не перегрузить dev-сервер. Цель —
|
||||
`127.0.0.1`, не `localhost` (резолвер Nuclei на этой машине пропускает `localhost`).
|
||||
Сначала быстрый smoke по тегу `tech`, затем полный прогон severity medium+.
|
||||
- **`php artisan serve`** нужен, чтобы портал был доступен сканеру; запускается
|
||||
фоном на локальном адресе, продуктовых данных и схемы не меняет.
|
||||
- **ZAP (#68)** требует Java-демона и при его отсутствии даёт `PENDING` — поэтому
|
||||
отдельным Bash-шагом не закладывается, его статус (`PENDING`/результат) фиксируется
|
||||
в отчёте шагом 7.
|
||||
- **Отчёт (шаг 7)** — единственная запись прогона: новый файл в `docs/security/`.
|
||||
Код, схема, миграции и git не трогаются; verify/criterion-гейты коммита не
|
||||
затрагиваются, так как коммита нет.
|
||||
|
||||
Известные долги F-T2 (`/api/admin/*` без app-гейта) и F-P1 (152-ФЗ:
|
||||
`deals.phones[]` не вычищаются) проверяются фактом в рамках шагов ПДн и угроз и
|
||||
явно отражаются в вердикте.
|
||||
@@ -0,0 +1,36 @@
|
||||
# План — дополнение операционного гайда уроками прогона
|
||||
|
||||
## Цель
|
||||
|
||||
Внести в существующий операционный гайд `docs/superpowers/router-mentor-wall-GUIDE.md`
|
||||
один новый раздел с двумя уроками прогона go-live security gate (контекст до
|
||||
печати плана; дефекты проверяющего и указателя) — аддитивной правкой, не трогая
|
||||
остальной текст и оглавление. Код, схема и git не затрагиваются.
|
||||
|
||||
```skills-json
|
||||
[]
|
||||
```
|
||||
|
||||
```steps-json
|
||||
[
|
||||
{"op":"Edit","object":"docs/superpowers/router-mentor-wall-GUIDE.md","ref":"G3"}
|
||||
]
|
||||
```
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":"docs/superpowers/router-mentor-wall-GUIDE.md","anchor":"Читать ПЕРЕД работой под стеной"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/superpowers/specs/2026-06-17-wall-guide-lessons-spec-v2.md","anchor":"весь контекст до печати плана"}
|
||||
]
|
||||
```
|
||||
|
||||
## Переговоры
|
||||
|
||||
### Круг 1
|
||||
|
||||
Правка единственная и аддитивная: один шаг Edit добавляет новый раздел второго
|
||||
уровня в операционный гайд рядом с разделом «Частые ошибки». Продуктовый код,
|
||||
схема БД, миграции и git не затрагиваются; навыки не требуются (skills-json пуст).
|
||||
Содержание раздела — два урока из спеки v2 (контекст до печати плана + наблюдаемые
|
||||
дефекты проверяющего/указателя). Гайд — обычный документ, не машинерия стены
|
||||
(не `enforce-*`/`judge-*`/`floor-*`), поэтому правится шагом плана штатно.
|
||||
@@ -71,6 +71,33 @@
|
||||
- **Наставник/судья async** (~25-32с). Печать встаёт не мгновенно. Per-attempt таймаут 90с
|
||||
(`HEAVY_LLM_TIMEOUT_MS`). degraded («не дозвонился») → просто повтор.
|
||||
|
||||
## Уроки живого прогона (go-live аудит 17.06.2026)
|
||||
|
||||
**Урок 1 — весь контекст ДО печати плана.** В режиме реализации (под опечатанным
|
||||
планом) чтение вне пути текущего шага блокируется гейтом ДР-1 — нельзя прочитать
|
||||
ни исходники для анализа, ни даже собственный вывод инструмента (temp-файл
|
||||
сканера). Поэтому всю разведку (схема, роуты, конфиги, reference-файлы навыков,
|
||||
вывод сканеров) собирай ДО печати плана — в разговорном режиме чтение свободно.
|
||||
**Запасной канал**, если контекст понадобился уже под планом: владелец вставляет
|
||||
содержимое файла прямо в чат — это контекст разговора, а не вызов Read, и
|
||||
дисциплина чтения его не трогает. Вывод: для аудита/разведки сперва читаешь всё
|
||||
нужное, потом печатаешь спеку→план, где шаги уже не требуют новых чтений.
|
||||
|
||||
**Урок 2 — дефекты проверяющего и указателя (знать заранее).**
|
||||
|
||||
- *Недетерминизм судьи.* Судья может выдать разный вердикт на байт-идентичном
|
||||
тексте между кругами (наблюдалось: круг N — NO-GO `[fatal]` на строке «Цель»,
|
||||
круг N+1 — GO на том же тексте). Лечение — просто повторить печать (async-повтор).
|
||||
- *Рассинхрон указателя (F-J на практике).* Ранний хук цепочки (`supreme-gate`)
|
||||
сдвигает указатель шага, а более поздний PreToolUse-блокер (skill-discipline)
|
||||
роняет само действие → шаг считается пройденным, хотя инструмент не выполнился
|
||||
(на прогоне так был пропущен первый шаг gitleaks). Перепечатка плана
|
||||
байт-в-байт НЕ сбрасывает указатель (тот же `plan_id`) — нужен план с другим
|
||||
именем/содержимым, либо `FLOOR-ESCAPE: plan-done`.
|
||||
- *Профилактика для скил-планов.* Объявленные в `skills-json` навыки вызови
|
||||
(Skill) ПЕРВЫМ делом после печати, ДО первого мутирующего/Bash-шага — иначе
|
||||
skill-discipline уронит первый шаг и уведёт указатель.
|
||||
|
||||
## ⛔ Нельзя: правка машинерии стены (F-K)
|
||||
|
||||
Файлы `tools/enforce-*.mjs`, `judge-*`, `mentor-*`, `floor-*`, `escape-grant`, `plan-lock` и т.п. —
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# Спека — Go-Live Security Gate (пред-прод прогон безопасности)
|
||||
|
||||
## Цель
|
||||
|
||||
Один воспроизводимый прогон безопасности портала Лидерра перед выходом в интернет:
|
||||
статический анализ, проверка персональных данных (152-ФЗ), модель угроз и
|
||||
динамическое сканирование локальной копии — с единым собранным вердиктом
|
||||
**GO / NO-GO** и письменным отчётом. Цель прогона — текущая ветка `main` на
|
||||
локальной копии `127.0.0.1:8000`; боевой контур не трогается.
|
||||
|
||||
## Охват и гарды {#D0}
|
||||
|
||||
**Контракт.** Прогон — security-only: выдаёт GO/NO-GO только по безопасности и не
|
||||
заменяет сквозной аудит портала (тесты/схема/UI/a11y/coverage). Все динамические
|
||||
проверки направлены строго на локальную копию `127.0.0.1` — боевой адрес не
|
||||
сканируется (гард IS8). Прогон не правит продуктовый код и не коммитит; единственная
|
||||
запись — файл отчёта в `docs/security/`.
|
||||
|
||||
**Edge-cases.** Если инструмент не установлен (ZAP без Java, Ward/Nuclei отсутствуют) —
|
||||
шаг фиксируется как `PENDING`, прогон продолжается, итог — «условный GO» с пометкой
|
||||
обязательной установки до публичного деплоя. Если локальный сервер не поднят —
|
||||
динамические шаги (Nuclei/ZAP) дают `PENDING`, а не ложный «чисто».
|
||||
|
||||
**Конвенция.** Severity → статус: `critical`/`high` любого инструмента = NO-GO;
|
||||
`medium` = предупреждение (фиксируется, не блокирует); `low`/`info` = информационно.
|
||||
|
||||
**Критерий.** Прогон считается выполненным, когда все пять шагов отработали (или
|
||||
отмечены `PENDING` с причиной) и собран отчёт с явным вердиктом GO/NO-GO,
|
||||
перечнем блокеров, предупреждений и PENDING-инструментов.
|
||||
|
||||
## Шаг 1 — Статика {#D1}
|
||||
|
||||
**Контракт.** Последовательно: gitleaks (секреты в истории), Semgrep (статика кода),
|
||||
Ward (конфиг/`.env`/зависимости/код Laravel). Каждый фиксирует результат в отчёте.
|
||||
|
||||
**Edge-cases.** gitleaks: учебная фикстура секрет-сканера — в allowlist gitleaks,
|
||||
не считается утечкой. Рабочее дерево содержит незакоммиченные правки параллельной
|
||||
сессии (карточка сделки F1/F2) — статика видит их; находки из незавершённого кода
|
||||
помечаются как таковые.
|
||||
|
||||
**Конвенция.** gitleaks: любая реальная утечка = NO-GO (critical). Semgrep: 0
|
||||
critical/high ожидаемо, medium — предупреждение. Ward: 0 critical; `APP_DEBUG=true`/
|
||||
`APP_ENV=local` на dev-копии — ожидаемы (не боевой `.env`), фиксируются как info.
|
||||
|
||||
**Критерий.** Три инструмента отработали, их вывод разнесён по severity в отчёт.
|
||||
|
||||
## Шаг 2 — ПДн / 152-ФЗ {#D2}
|
||||
|
||||
**Контракт.** Навык `pdn-152fz-audit` в обоих режимах: Режим 1 (технический) — RLS на
|
||||
таблицах ПДн, маскирование pg_anonymizer, отсутствие phone/email в логах и Sentry;
|
||||
Режим 2 (соответствие) — хранение в РФ, согласия, права субъекта (`pd_subject_requests`),
|
||||
журнал обработки (`pd_processing_log`), уведомление РКН.
|
||||
|
||||
**Edge-cases.** Известный долг F-P1 — `deals.phones[]` не вычищаются по сроку
|
||||
хранения — проверяется и явно отражается в вердикте (см. {#D6}).
|
||||
|
||||
**Конвенция.** Нарушение Режима 1 уровня critical (ПДн в открытых логах/Sentry) =
|
||||
NO-GO. Замечания Режима 2 — предупреждения, если не critical.
|
||||
|
||||
**Критерий.** Оба режима отработали, список нарушений (или «нет») в отчёте.
|
||||
|
||||
## Шаг 3 — Угрозы STRIDE {#D3}
|
||||
|
||||
**Контракт.** Навык `threat-model` — проверить, что топ-приоритетные STRIDE-угрозы
|
||||
закрыты контрмерами: rate-limit на вход, HMAC на webhook, Sanctum token-auth, CSRF,
|
||||
RLS. Известный долг F-T2 — `/api/admin/*` без app-гейта авторизации — проверяется
|
||||
явно (см. {#D6}).
|
||||
|
||||
**Edge-cases.** Если актуальной модели угроз за последние 30 дней нет — модель
|
||||
строится заново в этом прогоне.
|
||||
|
||||
**Конвенция.** Незакрытая топ-угроза = блокер либо принятый с владельцем риск;
|
||||
середина — предупреждение.
|
||||
|
||||
**Критерий.** Перечень незакрытых топ-угроз (или «нет») в отчёте.
|
||||
|
||||
## Шаг 4 — Динамика {#D4}
|
||||
|
||||
**Контракт.** Цель — локальная копия `http://127.0.0.1:8000` (гард IS8). Nuclei —
|
||||
широкое сканирование известных уязвимостей; ZAP — глубокое DAST (active scan).
|
||||
|
||||
**Edge-cases (обязательны).** Nuclei на native-Windows: цель строго `127.0.0.1`
|
||||
(не `localhost` — резолвер пропустит), низкий темп `-rate-limit 20 -c 5` для
|
||||
однопоточного `php artisan serve`. ZAP требует Java 17 + запущенного демона — при
|
||||
отсутствии шаг = `PENDING`. Сервер должен быть поднят до сканирования.
|
||||
|
||||
**Конвенция.** critical/high из Nuclei/ZAP = NO-GO; medium — разбор вручную
|
||||
(предупреждение).
|
||||
|
||||
**Критерий.** Динамические сканеры отработали по локальной цели (или `PENDING` с
|
||||
причиной), находки — в отчёт по severity.
|
||||
|
||||
## Шаг 5 — Вердикт и отчёт {#D5}
|
||||
|
||||
**Контракт.** Собрать находки всех шагов в один отчёт `docs/security/` с заголовком,
|
||||
датой, версией схемы, commit, целью и блоками по пяти шагам, завершаемый строкой
|
||||
вердикта GO/NO-GO.
|
||||
|
||||
**Edge-cases.** Любой PENDING-инструмент → вердикт «условный GO» с явным перечнем
|
||||
того, что обязано быть установлено до публичного деплоя.
|
||||
|
||||
**Конвенция.** Структура отчёта повторяет канон гейта: блоки ШАГ 1..4, затем
|
||||
`=== ВЕРДИКТ ===` с тремя списками — блокеры (critical/high), предупреждения (medium),
|
||||
PENDING-инструменты.
|
||||
|
||||
**Критерий.** Файл отчёта существует, содержит явный вердикт и три списка.
|
||||
|
||||
## Известные долги для явной проверки {#D6}
|
||||
|
||||
**Контракт.** Прогон обязан явно подтвердить или закрыть два interim-NO-GO из
|
||||
предыдущего доменного аудита: F-T2 (`/api/admin/*` без app-гейта авторизации) и
|
||||
F-P1 (152-ФЗ: `deals.phones[]` не вычищаются по сроку хранения).
|
||||
|
||||
**Edge-cases.** Если за время после аудита долг закрыт кодом — прогон это
|
||||
подтверждает фактом (нашли гейт/очистку), а не «по памяти».
|
||||
|
||||
**Конвенция.** Открытый F-T2 или F-P1 = блокер вердикта, пока не закрыт или не
|
||||
принят владельцем как осознанный риск.
|
||||
|
||||
**Критерий.** В отчёте по каждому из двух долгов — статус «закрыт фактом X» либо
|
||||
«открыт, блокер».
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":".claude/skills/security-go-live/references/gate.md","anchor":"IS8 — цель по умолчанию локальная"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/Tooling_v8_3.md","anchor":"security-go-live — go-live security-gate"}
|
||||
]
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
# Спека v2 — требования к дополнению операционного гайда уроками прогона
|
||||
|
||||
## Цель
|
||||
|
||||
Зафиксировать требования к дополнению операционного гайда
|
||||
`docs/superpowers/router-mentor-wall-GUIDE.md`: какие два урока прогона go-live
|
||||
security gate (17.06.2026) должны быть в нём отражены, с каким содержанием и в
|
||||
каком месте файла. Документ описывает контракт, edge-cases и критерии приёмки
|
||||
правки; сама правка выполняется отдельным шагом плана, не этим документом.
|
||||
|
||||
## Урок 1 — весь контекст до печати плана {#G1}
|
||||
|
||||
**Контракт.** В режиме реализации (под опечатанным планом) чтение вне пути
|
||||
текущего шага блокируется — нельзя прочитать ни исходники для анализа, ни даже
|
||||
собственный вывод инструмента (temp-файл сканера). Поэтому всю разведку (схема,
|
||||
роуты, конфиги, reference-файлы навыков, вывод сканеров) собирают ДО печати плана,
|
||||
в разговорном режиме, где чтение свободно.
|
||||
|
||||
**Edge-case / запасной канал.** Если контекст всё же понадобился в режиме
|
||||
реализации — владелец вставляет содержимое файла прямо в чат: это контекст
|
||||
разговора, а не вызов Read-инструмента, поэтому дисциплина чтения его не трогает.
|
||||
|
||||
**Конвенция.** Для аудита/разведки: сперва читаешь всё нужное, формулируешь
|
||||
находки, и только потом печатаешь спеку→план, где шаги уже не требуют новых чтений.
|
||||
|
||||
**Критерий приёмки правки.** В гайде есть пункт, явно предписывающий собрать
|
||||
чтения до печати плана и называющий приём «вставка в чат» как запасной канал.
|
||||
|
||||
## Урок 2 — наблюдаемые дефекты проверяющего и указателя {#G2}
|
||||
|
||||
**Контракт.** На прогоне зафиксированы два дефекта машинерии, о которых сессия
|
||||
должна знать заранее: (1) проверяющий план может выдать разный вердикт на
|
||||
байт-идентичном тексте между кругами (наблюдалось NO-GO с пометкой fatal на одной
|
||||
строке цели, затем GO на том же тексте); (2) рассинхрон указателя — ранний хук в
|
||||
цепочке сдвигает указатель шага, а более поздний хук роняет само действие, из-за
|
||||
чего шаг считается пройденным, хотя инструмент не выполнился (на прогоне так был
|
||||
пропущен первый шаг).
|
||||
|
||||
**Edge-case.** Перепечатка плана байт-в-байт не сбрасывает указатель (тот же
|
||||
идентификатор плана) — для сброса нужен план с другим именем/содержимым.
|
||||
|
||||
**Конвенция.** При недетерминированном вердикте — повторить печать (async-повтор);
|
||||
при рассинхроне указателя — новый план с другим именем либо завершить застрявший
|
||||
план резерв-меткой.
|
||||
|
||||
**Критерий приёмки правки.** В гайде есть пункт, называющий оба дефекта и
|
||||
отсылающий к уже описанным средствам восстановления (новый план / резерв-метка).
|
||||
|
||||
## Куда вписать {#G3}
|
||||
|
||||
**Контракт.** Новый раздел добавляется в существующий гайд как отдельная секция
|
||||
(заголовок второго уровня) рядом с разделом «Частые ошибки», не ломая остальную
|
||||
структуру и оглавление.
|
||||
|
||||
**Критерий приёмки правки.** Раздел присутствует в файле гайда; прежний текст не
|
||||
повреждён.
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":"docs/superpowers/router-mentor-wall-GUIDE.md","anchor":"Читать ПЕРЕД работой под стеной"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}
|
||||
]
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
# Спека — уроки прогона аудита в операционный гайд
|
||||
|
||||
## Цель
|
||||
|
||||
Добавить в операционный гайд `docs/superpowers/router-mentor-wall-GUIDE.md`
|
||||
короткий раздел с уроками, выявленными на живом многошаговом прогоне (go-live
|
||||
security gate 17.06.2026): порядок сбора контекста перед многошаговой работой,
|
||||
особенности чтения вывода инструментов и наблюдаемые дефекты проверяющего/
|
||||
указателя. Цель — чтобы следующая сессия не повторяла те же грабли.
|
||||
|
||||
## Урок 1 — весь контекст до печати плана {#G1}
|
||||
|
||||
**Контракт.** В режиме реализации (под опечатанным планом) чтение вне пути
|
||||
текущего шага блокируется — нельзя прочитать ни исходники для анализа, ни даже
|
||||
собственный вывод инструмента (temp-файл сканера). Поэтому всю разведку (схема,
|
||||
роуты, конфиги, reference-файлы навыков, вывод сканеров) собирают ДО печати плана,
|
||||
в разговорном режиме, где чтение свободно.
|
||||
|
||||
**Edge-case / обход.** Если контекст всё же понадобился в режиме реализации —
|
||||
владелец вставляет содержимое файла прямо в чат: это контекст разговора, а не
|
||||
вызов Read-инструмента, поэтому дисциплина чтения его не трогает.
|
||||
|
||||
**Конвенция.** Для аудита/разведки: сперва читаешь всё нужное, формулируешь
|
||||
находки, и только потом печатаешь спеку→план, где шаги уже не требуют новых чтений.
|
||||
|
||||
**Критерий.** В гайде есть пункт, явно предписывающий собрать чтения до печати
|
||||
плана и называющий приём «вставка в чат» как запасной канал.
|
||||
|
||||
## Урок 2 — наблюдаемые дефекты проверяющего и указателя {#G2}
|
||||
|
||||
**Контракт.** На прогоне зафиксированы два дефекта машинерии, о которых сессия
|
||||
должна знать заранее: (1) проверяющий план может выдать разный вердикт на
|
||||
байт-идентичном тексте между кругами (наблюдалось NO-GO с пометкой fatal на одной
|
||||
строке цели, затем GO на том же тексте); (2) рассинхрон указателя — ранний хук в
|
||||
цепочке сдвигает указатель шага, а более поздний хук роняет само действие, из-за
|
||||
чего шаг считается пройденным, хотя инструмент не выполнился (на прогоне так был
|
||||
пропущен первый шаг).
|
||||
|
||||
**Edge-case.** Перепечатка плана байт-в-байт не сбрасывает указатель (тот же
|
||||
идентификатор плана) — для сброса нужен план с другим именем/содержимым.
|
||||
|
||||
**Конвенция.** При недетерминированном вердикте — повторить печать (это уже
|
||||
описано как async-повтор); при рассинхроне указателя — новый план с другим именем
|
||||
либо завершить застрявший план резерв-меткой.
|
||||
|
||||
**Критерий.** В гайде есть пункт, называющий оба дефекта и отсылающий к уже
|
||||
описанным средствам восстановления (новый план / резерв-метка завершения).
|
||||
|
||||
## Куда вписать {#G3}
|
||||
|
||||
**Контракт.** Новый раздел добавляется в существующий гайд как отдельная секция
|
||||
(заголовок второго уровня) рядом с разделом «Частые ошибки», не ломая остальную
|
||||
структуру и оглавление.
|
||||
|
||||
**Критерий.** Раздел присутствует в файле гайда; прежний текст не повреждён.
|
||||
|
||||
```verified-context-json
|
||||
[
|
||||
{"id":"vc1","kind":"EXTRACTED","ref":"docs/superpowers/router-mentor-wall-GUIDE.md","anchor":"Читать ПЕРЕД работой под стеной"},
|
||||
{"id":"vc2","kind":"EXTRACTED","ref":"docs/security/2026-06-17-go-live-security-report.md","anchor":"SECURITY GO-LIVE REPORT"}
|
||||
]
|
||||
```
|
||||
Reference in New Issue
Block a user