randomayzer/agents/antigravity/done/TASK-2026-08-20-seed-precommit.md

6.7 KiB
Raw Blame History

Phase 2.4 — Seed Pre-Commit Gate Report (Eliminating Seed Grinding)

Date: 2026-08-20
Base Commit SHA: 9927e74421223135a170de640255803ab513fd48
Status: COMPLETED / PASS
Assigned Agent: Antigravity (Implementation Orchestrator)


1. Executive Summary

Закрыта критическая уязвимость манипуляции результатами розыгрышей (Seed Grinding / Pre-computation attack), при которой организатор мог локально перебрать seed'ы на открытом списке участников и передать в POST /api/giveaways/[id]/draw подобранный seed, гарантирующий победу нужного участника при успешном статусе верификации verified: true.

Реализована схема Cryptographic Seed Pre-Commitment:

  1. Клиентский seed полностью исключён из входных схем (createGiveawaySchema, executeDrawSchema). Попытка передать seed в теле запроса строго отклоняется со статусом 400 VALIDATION_ERROR.
  2. Seed генерируется на сервере исключительно через CSPRNG (crypto.randomBytes(16).toString('hex')) в момент создания и блокировки неизменяемого слепка участников (createAndLockSnapshot) и сохраняется в БД (Giveaway.seed) в единой атомарной транзакции.
  3. До момента проведения жеребьёвки (DRAWN) открытый seed скрыт от клиента во всех публичных и приватных эндпоинтах (POST /api/giveaways/[id]/snapshot, GET /api/giveaways/[id], GET /api/giveaways, GET /api/giveaways/[id]/participants). Клиенту отдаётся только криптографическое обязательство seedCommitment = sha256(seed).
  4. Роут жеребьёвки POST /api/giveaways/[id]/draw читает seed строго из базы данных (giveaway.seed). Любой fallback на генерацию seed в роуте жеребьёвки удалён. Если seed отсутствует — возвращается 409 CONFLICT.
  5. После завершения жеребьёвки seed раскрывается публично (giveaway.seed и drawResult.seedUsed), позволяя любому участнику подтвердить равенство sha256(drawResult.seedUsed) === seedCommitment и математическую честность через независимый GET /api/giveaways/[id]/verify.

2. Modified Files

File Type Description
src/core/randomizer/hasher.ts Backend Добавлена функция computeSeedCommitment(seed: string): string (SHA-256 hex digest).
src/core/validation/giveaway-schemas.ts Validation Удалено поле seed из createGiveawaySchema и executeDrawSchema (строгая валидация).
src/lib/repository/giveaway-repository.ts Repository Добавлено поле seedCommitment?: string | null в GiveawayWithRelations, удален seed из CreateGiveawayInput.
src/lib/repository/memory-repository.ts Storage Driver Инициализация seed: null, атомарная генерация и фиксация seed + seedCommitment в createAndLockSnapshot.
src/lib/repository/prisma-repository.ts Storage Driver Фиксация seed в БД внутри $transaction при createAndLockSnapshot, маппинг seedCommitment.
src/app/api/giveaways/route.ts API Route Удалена передача клиентского seed при создании розыгрыша.
src/app/api/giveaways/[id]/snapshot/route.ts API Route Возврат seedCommitment вместо раскрытия plaintext seed.
src/app/api/giveaways/[id]/draw/route.ts API Route Строгое чтение pre-committed seed из БД; 409 CONFLICT при отсутствии; удалён fallback.
src/app/api/giveaways/[id]/route.ts API Route Маскирование seed: null до статуса DRAWN, отдача seedCommitment.
src/app/giveaways/new/page.tsx Frontend UI Удалено поле ручного ввода seed из шага 4; добавлен индикатор защиты от подбора (Seed Pre-Commitment) со значением SHA-256 commitment.
tests/api-validation.test.ts Tests Обновлены тесты валидации на строгое отклонение seed.
tests/seed-precommit-gate.test.ts Tests (NEW) Комплексный adversarial & regression test suite (7 тестов).

3. Core Cryptographic Invariants Preserved

Ни один из базовых криптографических алгоритмов НЕ изменялся:

  • HMAC_SHA256_FY_V1
  • DeterministicHmacStream
  • executeDeterministicDrawV1
  • computeParticipantsSnapshotHash
  • computeConditionsHash
  • computeDeterministicProofHash
  • computeAuditEventHash
  • verifyDrawResult

4. Verification Evidence & Test Gate

Фактически выполненные команды:

  1. npx prisma generate → Exit code 0 (Prisma Client v5.22.0 generated).
  2. npm test → Exit code 0 (48 test files, 280 tests passed, 0 failed).
  3. npm run lint → Exit code 0 (Next.js ESLint passed clean).
  4. npm run build → Exit code 0 (Production build & static generation compiled successfully).

Regression Tests Summary (tests/seed-precommit-gate.test.ts):

  • adversarial attempt to pass custom seed in draw body fails with 400 and keeps status SNAPSHOT_LOCKED → PASS
  • grinding regression: local brute-force of 100 seeds cannot alter the pre-committed API winner → PASS
  • draw attempt on giveaway without locked snapshot and seed returns 409 Conflict → PASS
  • GET /api/giveaways/[id] masks seed before DRAWN and exposes seedCommitment → PASS
  • after DRAWN, sha256(seedUsed) strictly equals seedCommitment and verify endpoint succeeds → PASS
  • MemoryGiveawayRepository generates and locks seed during createAndLockSnapshot → PASS
  • PrismaGiveawayRepository maps seedCommitment correctly → PASS

5. Security & Risk Assessment

  • CRITICAL/HIGH findings: 0 open
  • Seed Grinding Attack: ELIMINATED & MATHEMATICALLY PREVENTED
  • IDOR / Cross-User Access: PRESERVED (protected by session & requireGiveawayOwner)
  • Secrets: 0 leaked
  • UNVERIFIED statements: None