57 lines
4.9 KiB
Markdown
57 lines
4.9 KiB
Markdown
# Task 13: Политика анонимного доступа к POST /api/posts/preview Report
|
||
|
||
**Date:** 2026-08-21
|
||
**Base Commit SHA:** `76ab7d0e77d06cbc31b66fc5c0cdd80282d7f496`
|
||
**Status:** COMPLETED / PASS
|
||
**Assigned Agent:** Antigravity (Implementation Orchestrator)
|
||
|
||
---
|
||
|
||
## 1. Исправление предыдущего утверждения (Correction of Finding)
|
||
|
||
В отчёте по Заданию 05 (`agents/antigravity/done/TASK-2026-08-21-05-post-preview-auth.md:37`) содержалось ошибочное утверждение:
|
||
> *"Quota Protection: Невозможно истощить квоту приложения анонимными запросами благодаря строгому ограничению IP."*
|
||
|
||
**Фактический анализ:**
|
||
1. При `TRUST_PROXY=true` злоумышленник с пулом IP-адресов мог линейно расходовать квоту `VK_SERVICE_TOKEN`, обходя per-IP лимитер.
|
||
2. При дефолтной конфигурации (`TRUST_PROXY` не задан, пустой `req.ip`) все анонимные клиенты делили один ключ `direct-client`. Превышение 15 запросов одним пользователем блокировало превью всем остальным анонимам, создавая DoS.
|
||
3. Весь сценарий визарда создания розыгрыша в интерфейсе (`handleFetchPost` -> `POST /api/giveaways`) требует авторизации через VK ID (`requireAuthenticatedUser`), поэтому анонимный доступ к превью не обслуживал ни один завершаемый пользовательский сценарий.
|
||
|
||
---
|
||
|
||
## 2. Принятое архитектурное решение: Вариант A (Обязательная аутентификация)
|
||
|
||
1. **Защита маршрута `POST /api/posts/preview` (`src/app/api/posts/preview/route.ts`):**
|
||
- На маршрут установлен вызов `const sessionUser = await requireAuthenticatedUser(req)`.
|
||
- Анонимные запросы без сессионной куки немедленно отклоняются с кодом `401 Unauthorized`.
|
||
- Запросы без авторизации **никогда не обращаются к VK API / провайдеру** (0 вызовов VK API, что математически и аппаратно доказано mock-счетчиками в тестах).
|
||
- Лимитирование переведено на `user-scoped` ключ организатора: `generalApiRateLimiter.assertAllowed('post-preview:user:' + sessionUser.id)` (120 запросов / мин).
|
||
|
||
2. **Интерфейс пользователя (`src/app/giveaways/new/page.tsx`):**
|
||
- Клиентский визард корректно обрабатывает 401 на этапе предпросмотра поста и выводит понятное сообщение: «Для создания розыгрыша и предпросмотра публикации необходимо войти через VK ID.» со ссылкой на вход, сохраняющей целевой URL.
|
||
|
||
---
|
||
|
||
## 3. Modified & Created Files
|
||
|
||
| File | Status | Description |
|
||
|------|--------|-------------|
|
||
| `src/app/api/posts/preview/route.ts` | MODIFIED | Установлен `requireAuthenticatedUser(req)` и user-scoped rate limiter. |
|
||
| `tests/preview-quota-policy.test.ts` | NEW | 4 теста: отсечение анонимов с 401 и 0 вызовов VK API, успешный preview для организатора, user-scoped лимит, CSRF-защита. |
|
||
| `tests/post-preview-guard.test.ts` | MODIFIED | Тест 3 обновлен под обязательную аутентификацию (401). |
|
||
| `tests/rate-limit-identity.test.ts` | MODIFIED | Тестирование `TRUST_PROXY=true` переведено на публичный эндпоинт `GET /api/giveaways/[id]/public`. |
|
||
| `tests/security.test.ts` | MODIFIED | В тест утечки токенов добавлена сессия организатора. |
|
||
| `tests/effective-capabilities-truthfulness.test.ts` | MODIFIED | Тест 4 обновлен для авторизованного пользователя без кастомных токенов. |
|
||
|
||
---
|
||
|
||
## 4. Verification Evidence
|
||
|
||
```text
|
||
npx prisma generate -> EXIT 0 (Prisma Client v5.22.0)
|
||
npx tsc --noEmit -> EXIT 0 (0 ошибок типизации во всех 59 тест-файлах и исходном коде)
|
||
npm test -> EXIT 0 (59 тест-сьютов, 342 теста пройдены успешно)
|
||
npm run lint -> EXIT 0 (0 ошибок, 6 warnings на no-img-element)
|
||
npm run build -> EXIT 0 (Все 17 маршрутов скомпилированы успешно в Next.js 16.3.2)
|
||
npm audit --omit=dev -> EXIT 0 (0 vulnerabilities)
|
||
```
|