# Спека v2 — middleware безопасных HTTP-заголовков (закрытие ZAP Medium anti-clickjacking)

## Цель

Добавить Laravel-middleware, проставляющий безопасные HTTP-заголовки на все
web-ответы портала, чтобы закрыть Medium-находку go-live аудита 17.06.2026
«Missing Anti-clickjacking Header» (плюс базовые `X-Content-Type-Options`,
`Referrer-Policy`). Реализация по TDD; CSP в этот объём НЕ входит (отдельная
выверенная задача под Vue+Vuetify SPA).

## H1 — какие заголовки ставит middleware {#H1}

**Контракт.** Новый класс `App\Http\Middleware\SecurityHeaders` в `handle()` после
`$next($request)` проставляет на ответе:
- `X-Frame-Options: SAMEORIGIN` (анти-clickjacking);
- `X-Content-Type-Options: nosniff`;
- `Referrer-Policy: strict-origin-when-cross-origin`.

**Edge-case.** Заголовки ставятся без условий на все ответы web-группы; уже
существующие значения перезаписываются предсказуемо (`headers->set`).

**Конвенция.** Стиль файла — как у прочих middleware: `declare(strict_types=1)`,
namespace `App\Http\Middleware`, сигнатура `handle(Request, Closure): Response`
(Symfony Response).

**Критерий.** Класс существует, в `handle()` выставлены ровно три заголовка.

## H2 — регистрация {#H2}

**Контракт.** Middleware регистрируется глобально на web-группу в
`bootstrap/app.php` внутри `->withMiddleware(...)` через
`$middleware->web(append: [SecurityHeaders::class])` (импорт класса сверху, как
`SetTenantContext`/`EnsureSaasAdmin`). Все маршруты портала — в web-группе, значит
покрытие полное.

**Критерий.** В `bootstrap/app.php` есть append SecurityHeaders в web-группу.

## H3 — тест (TDD) {#H3}

**Контракт.** Pest-feature-тест `tests/Feature/SecurityHeadersTest.php` бьёт по
публичному маршруту `/` (welcome SPA, без auth/БД) и проверяет наличие и значения
трёх заголовков. Сначала RED (middleware ещё нет), после реализации — GREEN.

**Edge-case.** Маршрут `/` отдаёт 200 (Route::view welcome), БД не требуется —
тест лёгкий и не зависит от тестовой базы.

**Критерий.** Тест есть; до реализации падает, после — проходит.

## H4 — CSP вне объёма {#H4}

**Контракт.** `Content-Security-Policy` в этом middleware НЕ ставится: корректная
политика под Vue 3 + Vuetify 3 SPA (script-src/style-src/inline) требует выверки и
прогона по экранам, иначе ломает интерфейс. Фиксируется как отдельная задача.

**Критерий.** В коде middleware CSP отсутствует; в комментарии класса — пометка,
что CSP вынесен отдельно.

## Переговоры

### Круг 1

Прошу наставника при ревью зафиксировать содержательную forward-`recommendation`:
на что обратить внимание при реализации — порядок и состав трёх заголовков (H1),
полнота покрытия через web-append (H2), что тест бьёт публичный `/` без БД (H3),
и что CSP осознанно вынесен в отдельную задачу (H4). Это нужно, чтобы вердикт был
содержательным, а не с пустым слотом рекомендации.

```verified-context-json
[
  {"id":"vc1","kind":"EXTRACTED","ref":"app/bootstrap/app.php","anchor":"withMiddleware(function"},
  {"id":"vc2","kind":"EXTRACTED","ref":"app/app/Http/Middleware/SetTenantContext.php","anchor":"Устанавливает app.current_tenant_id"}
]
```
