# План: доменный go-live аудит (ревизия I) — обход greps закрыт механически

## Цель

Записать **промежуточный неавторизующий** отчёт доменного аудита по спеке
`2026-06-15-go-live-security-audit-evidence-3.md` (раздел D5). Порядок строго прямой:
**фактический прогон greps 1–12 → правило агрегации → Write выводит отчёт (шаг 13)**.
Каждая находка имеет шесть самостоятельных полей, включая **рекомендация**. Блокеры = все
открытые P1 = **{F-T2, F-P1}**. Форма — **не авторизация go-live**; финал — владелец после
live-DAST. Открытые вопросы (Б-1/SEC/CTO/Биз/Диз/DO) не закрываются.

## Защита от обхода greps (ответ на премортем abuse) — НЕОБХОДИМО

1. **Обход механически невозможен.** Стена исполняет шаги `steps-json` строго по порядку:
   указатель НЕ достигает Write-шага (13), пока фактически не исполнены шаги 1–12. Пропустить
   grep-шаги и сразу записать отчёт нельзя — это запрещено механизмом печати.
2. **verified-context-json ≠ собранная доказательная база.** Его якоря доказывают только
   **заземление** плана (подстроки реально существуют в указанных файлах). Это НЕ замена
   прогону greps и НЕ готовая доказательная база отчёта.
3. **Доказательная база отчёта = ТОЛЬКО фактические выводы greps**, наблюдённые на исполнении
   шагов 1–12. Write обязан вписать в каждую находку буквальный вывод её grep-шага; писать
   отчёт из шаблона/контекста **без** прогона greps — ЗАПРЕЩЕНО.
4. Это аудит go-live (деньги и ПДн необратимы) — верификация фактическим прогоном обязательна,
   не опциональна.

## Правило агрегации (управляет Write-шагом)

Для каждой находки Write читает фактический вывод её grep-шага:

- ожидаемый дискриминирующий вывод → `подтверждён`, факт. вывод → в доказательную базу;
- иной вывод (опроверг гипотезу) → `requires-manual-check` (гипотеза не выдаётся за факт);
- grep по отсутствию (шаги 5, 10 — ожидается пусто): пусто → `observation` («вывод
  отсутствует», отсутствие не заякоривается как факт); непусто → `requires-manual-check`;
- контр-гипотеза «чисто» (шаги 6, 11): подтверждено → раздел «Чисто», не находка; иначе →
  находка `requires-manual-check`.

## Роль скилов: интерпретирующий метод (не шаги steps-json)

`steps-json` допускает op ∈ {Write, Edit, Bash, MultiEdit} — Skill не входит, скилы не могут
быть шагами. Документирующая задача: скилы — линза интерпретации grep-evidence (как
`test-driven-development` ведёт код). Порядок строго фиксирован: после шага 12 и **до** Write,
последовательно `threat-model` → `pdn-152fz-audit` → `billing-audit` → `security-go-live` (не
параллельно, не до greps). Выводы материализуются Write: threat-model→F-T2/F-T1/F-T3,
pdn-152fz-audit→F-P1/F-P2, billing-audit→F-B1+«Чисто», security-go-live→вердикт (NO-GO,
блокеры {F-T2,F-P1}; контракт GO/NO-GO соблюдён, «промежуточный» = не финально-авторизующий).

## Сопоставление шагов и находок

| Шаг | grep | Тип | Находка | Дискриминирует |
|----|------|-----|---------|----------------|
| 1 | `EnsureSaasAdmin` в app/app·app/bootstrap·app/routes | discovery | F-T2 | где применён гейт; найден в роут-биндинге → в цепочке; иначе → requires-manual-check |
| 2 | `next` в EnsureSaasAdmin | факт | F-T2 | тело `return $next($request);` |
| 3 | `admin_user_id` в ResolvesAdminUserId | факт | F-T2 | id из `$request->input` |
| 4 | `Http::timeout` в WebhookSettingsController | факт | F-T1 | `Http::timeout(10)` |
| 5 | `withoutRedirecting` там же | absence | F-T1 | пусто → флаг не выставлен; непусто → requires-manual-check |
| 6 | `blockReason` в WebhookUrlGuard | чисто | (Чисто) | guard добротен |
| 7 | `verifyIpAllowlist` в SupplierWebhookController | факт | F-T3 | приёмник + fail-open |
| 8 | `contact_name` в PdErasureService | факт | F-P1 | erasure без phones[] |
| 9 | `phones` в schema.sql | факт | F-P1 | `phones JSONB` |
| 10 | `sentry` в app/config | absence | F-P2 | пусто → нет конфига (observation); непусто → requires-manual-check |
| 11 | `bcsub` в LedgerService | чисто | (Чисто) | деньги bcmath |
| 12 | `lead_charges_deal_id` в schema.sql | факт | F-B1 | `CREATE INDEX` (не `UNIQUE`) |
| 13 | Write отчёта | — | все | выводит отчёт по правилу из фактических выводов 1–12 |

ref шагов = якоря секций спеки {#D1}..{#D5}. Файл-артефакт в `docs/superpowers/audits/` —
директория существует, Write не упадёт по пути.

## Карточки навыков (верифицированные — дословно из `docs/registry/contracts/`)

```json
[
  {"skill":"threat-model","kind":"own","needs":["портал для моделирования угроз перед публикацией"],"produces":["STRIDE-модель: attack surface + приоритеты защиты"],"constraints":["self-authored; STRIDE going-public","ADR-014 IS6 НЕ Trail of Bits #39"],"preview-form":"outline","defaults":["карта точек входа → приоритизация по STRIDE"],"key-decisions":["что защищать первым перед публикацией"],"acceptance-criteria":["attack surface картирован, защита приоритизирована"]},
  {"skill":"pdn-152fz-audit","kind":"own","needs":["задача аудита ПДн / 152-ФЗ"],"produces":["аудит ПДн: инвентаризация, согласия, маскирование, логи доступа, pd_subject_request"],"constraints":["self-authored; режим техника + закон","ADR-014: IS4 НЕ pg_anonymizer #29, IS5 НЕ D2"],"preview-form":"outline","defaults":["инвентаризация ПДн в схеме/коде → проверка соответствия"],"key-decisions":["режим: техника или закон"],"acceptance-criteria":["ПДн инвентаризированы, соответствие 152-ФЗ проверено"]},
  {"skill":"billing-audit","kind":"own","needs":["код/диф биллинга для аудита денежной корректности"],"produces":["отчёт о money-инвариантах биллинга"],"constraints":["self-authored; аудит кода биллинга","ADR-012: НЕ налоги, НЕ процесс, НЕ security"],"preview-form":"outline","defaults":["проверять потерю копеек, двойное списание, tier-резолюцию, дрейф reconcile"],"key-decisions":["какие денежные инварианты в scope"],"acceptance-criteria":["каждый денежный путь проверен на потерю копеек и двойное списание"]},
  {"skill":"security-go-live","kind":"own","needs":["портал перед выходом в интернет (go/no-go)"],"produces":["вердикт GO/NO-GO + сводка проверок безопасности"],"constraints":["self-authored оркестратор #68-72 + D3","ADR-014 IS7 НЕ audit-portal"],"preview-form":"outline","defaults":["оркеструет ZAP/Nuclei/Ward/pdn-152fz/threat-model + Semgrep"],"key-decisions":["go/no-go порог"],"acceptance-criteria":["вердикт GO/NO-GO обоснован результатами проверок"]}
]
```

## Проектная форма отчёта (ШАБЛОН структуры; значения Write подставляет из фактических greps)

Поля «Статус» и «Доказательная база» Write подставляет из ФАКТИЧЕСКИХ выводов greps по правилу
агрегации. Гипотезы-приоритеты показаны для структуры; реальный вывод grep подтверждает или
понижает до `requires-manual-check`.

```markdown
# Промежуточный доменный go-live аудит безопасности Лидерры (статический, неавторизующий)

Дата 15.06.2026. Статус: промежуточный, НЕ авторизация go-live (финал — владелец после
live-DAST + поднятый сервер). Открытые вопросы не закрыты. Метод: threat-model (STRIDE) +
pdn-152fz-audit + billing-audit, сведено security-go-live.

| ID   | Инвариант | Файл:строка                       | Приоритет | Статус (из grep) | Рекомендация                          |
|------|-----------|-----------------------------------|-----------|------------------|---------------------------------------|
| F-T2 | D1        | EnsureSaasAdmin.php:34             | P1        | ←шаги 1-3        | вернуть app-гейт + admin id из сессии  |
| F-P1 | D3        | PdErasureService.php:167          | P1        | ←шаги 8-9        | чистить deals.phones[] JSONB           |
| F-T1 | D2        | WebhookSettingsController.php:150 | P2        | ←шаги 4-5        | ->withoutRedirecting()                 |
| F-T3 | D2        | SupplierWebhookController.php      | P3        | ←шаг 7           | body-HMAC + fail-closed allowlist      |
| F-P2 | D3        | config/sentry.php (наличие?)      | P3        | ←шаг 10 (observation) | send_default_pii=false + скраббер  |
| F-B1 | D4        | schema.sql lead_charges index     | P3        | ←шаг 12          | UNIQUE на lead_charges                 |

Доказательная база: под каждой находкой — буквальный фактический вывод её grep-шага (вписывает
Write на исполнении). Чисто: шаг 6 (blockReason добротен), шаг 11 (bcmath), идемпотентность
lockForUpdate+PK, логи без ПДн, /api/managers и /api/webhooks/test под auth+гард, lead-region
в копии отсутствует. Вердикт (security-go-live): промежуточный NO-GO, блокеры {F-T2, F-P1} —
НЕ авторизация go-live; остаточные риски F-T1/F-T3/F-P2/F-B1 к устранению до публикации.
```

## Порядок исполнения

Шаги 1–12 (Bash grep) исполняются стеной по порядку и дают фактический вывод. Затем
`threat-model` → `pdn-152fz-audit` → `billing-audit` → `security-go-live` интерпретируют.
Шаг 13 (Write) выводит отчёт из фактических выводов 1–12 по правилу агрегации в файл-артефакт.

```skills-json
["threat-model", "pdn-152fz-audit", "billing-audit", "security-go-live"]
```

```steps-json
[
  {"op":"Bash","object":"grep -rn EnsureSaasAdmin app/app app/bootstrap app/routes","ref":"D1"},
  {"op":"Bash","object":"grep -n next app/app/Http/Middleware/EnsureSaasAdmin.php","ref":"D1"},
  {"op":"Bash","object":"grep -n admin_user_id app/app/Http/Controllers/Concerns/ResolvesAdminUserId.php","ref":"D1"},
  {"op":"Bash","object":"grep -n Http::timeout app/app/Http/Controllers/Api/WebhookSettingsController.php","ref":"D2"},
  {"op":"Bash","object":"grep -n withoutRedirecting app/app/Http/Controllers/Api/WebhookSettingsController.php","ref":"D2"},
  {"op":"Bash","object":"grep -n blockReason app/app/Support/WebhookUrlGuard.php","ref":"D2"},
  {"op":"Bash","object":"grep -n verifyIpAllowlist app/app/Http/Controllers/Api/SupplierWebhookController.php","ref":"D2"},
  {"op":"Bash","object":"grep -n contact_name app/app/Services/Pd/PdErasureService.php","ref":"D3"},
  {"op":"Bash","object":"grep -n phones db/schema.sql","ref":"D3"},
  {"op":"Bash","object":"grep -rn sentry app/config","ref":"D3"},
  {"op":"Bash","object":"grep -n bcsub app/app/Services/Billing/LedgerService.php","ref":"D4"},
  {"op":"Bash","object":"grep -n lead_charges_deal_id db/schema.sql","ref":"D4"},
  {"op":"Write","object":"docs/superpowers/audits/2026-06-15-portal-domain-audit-report.md","ref":"D5"}
]
```

```verified-context-json
[
  {"id":"D1a","kind":"EXTRACTED","ref":"app/app/Http/Middleware/EnsureSaasAdmin.php","anchor":"return $next($request);"}
]
```
