fix(validation): Task 14 reject excludeDuplicateComments in API schemas and upgrade CI node-version to 22
This commit is contained in:
parent
6510ea2a04
commit
8e39ce0cbd
7 changed files with 128 additions and 4 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
|
|
|
|||
47
agents/antigravity/done/TASK-2026-08-21-14-low-followups.md
Normal file
47
agents/antigravity/done/TASK-2026-08-21-14-low-followups.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Task 14: Два LOW-хвоста из ревью 9061488 Report
|
||||
|
||||
**Date:** 2026-08-21
|
||||
**Base Commit SHA:** `6510ea2a048aa9bbeec5af8d80e61a420f32c471`
|
||||
**Status:** COMPLETED / PASS
|
||||
**Assigned Agent:** Antigravity (Implementation Orchestrator)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
Устранены два LOW-хвоста, выявленные в ходе независимого ревью:
|
||||
|
||||
1. **Запрет `excludeDuplicateComments` на границе API (`src/core/validation/giveaway-schemas.ts`):**
|
||||
- Поле `excludeDuplicateComments` удалено из `filterRulesSchema`.
|
||||
- Поскольку `filterRulesSchema` определена со `.strict()`, любая попытка внешнего API-клиента передать `excludeDuplicateComments` при создании розыгрыша (`POST /api/giveaways`), импорте участников (`POST /api/giveaways/[id]/participants`) или фиксации слепка (`POST /api/giveaways/[id]/snapshot`) немедленно отвергается с кодом `400 Bad Request` (`ValidationError: Unrecognized key(s) in object: 'excludeDuplicateComments'`).
|
||||
- Поле физически не может попасть в `filterRulesSnapshot` нового розыгрыша и изменить `conditionsHash`.
|
||||
- **Обратная совместимость legacy-снапшотов:** Функция `computeConditionsHash` (`src/core/randomizer/canonical.ts:46`) и верификатор `verifyDrawResult` сохраняют поддержку старых слепков, где это поле было записано до Задания 07 (`verified: true`, `conditionsIntegrity: true`).
|
||||
|
||||
2. **Обновление версии Node.js в CI (`.github/workflows/ci.yml`):**
|
||||
- Версия `node-version` в GitHub Actions поднята с 20 до 22 (текущая Active LTS).
|
||||
- Все шаги CI (Prisma generate/migrations, Unit/Integration tests, ESLint, Next.js build) валидированы.
|
||||
|
||||
---
|
||||
|
||||
## 2. Modified Files
|
||||
|
||||
| File | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| `src/core/validation/giveaway-schemas.ts` | MODIFIED | Удалено `excludeDuplicateComments` из `filterRulesSchema`. |
|
||||
| `.github/workflows/ci.yml` | MODIFIED | Обновлен `node-version: 22` в CI workflow. |
|
||||
| `tests/duplicate-comments-rule.test.ts` | MODIFIED | Добавлены тесты отсечения `excludeDuplicateComments` на уровне схем Zod. |
|
||||
| `tests/auth-guard.test.ts` | MODIFIED | Удалено устаревшее поле из тестовых фикстур `validPostData`. |
|
||||
| `tests/security.test.ts` | MODIFIED | Удалено устаревшее поле из тестовой фикстуры. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Verification Evidence
|
||||
|
||||
```text
|
||||
npx prisma generate -> EXIT 0 (Prisma Client v5.22.0)
|
||||
npx tsc --noEmit -> EXIT 0 (0 ошибок типизации)
|
||||
npm test -> EXIT 0 (59 тест-сьютов, 343 теста пройдены успешно)
|
||||
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)
|
||||
```
|
||||
24
agents/antigravity/inbox/TASK-2026-08-21-14-low-followups.md
Normal file
24
agents/antigravity/inbox/TASK-2026-08-21-14-low-followups.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Task 14: Два LOW-хвоста из ревью 9061488
|
||||
|
||||
**Assigned to:** Antigravity (Implementation Orchestrator)
|
||||
**Priority:** LOW
|
||||
**Date:** 2026-08-21
|
||||
**Base SHA:** `6510ea2a048aa9bbeec5af8d80e61a420f32c471`
|
||||
|
||||
## Проблемы
|
||||
1. **`excludeDuplicateComments` на входе API (`filterRulesSchema`)**:
|
||||
- `src/core/validation/giveaway-schemas.ts` по-прежнему объявляет `excludeDuplicateComments: z.boolean().optional()`.
|
||||
- Если внешний API-клиент передаст этот флаг, он попадет в `filterRulesSnapshot` и в `computeConditionsHash`, изменив хеш условий для нового розыгрыша, хотя фактически на фильтрацию не влияет.
|
||||
- Необходимо удалить поле из `filterRulesSchema` (или отбрасывать/валидировать), сохранив чтение legacy-снапшотов в `computeConditionsHash` и `verifyDrawResult`.
|
||||
2. **`node-version: 20` в `.github/workflows/ci.yml`**:
|
||||
- Обновить версию Node.js в CI до актуальной активной LTS (Node 22 / 24) с сохранением поддержки `engines.node >= 20.9.0`.
|
||||
|
||||
## Scope
|
||||
1. Схема `filterRulesSchema`:
|
||||
- Удалить `excludeDuplicateComments` из `filterRulesSchema` (так как схемы используют `.strict()`, передача этого поля будет отвергаться с 400 ValidationError, либо при strip отбрасываться без записи в БД).
|
||||
- Сохранить в `FilterRules` тип `excludeDuplicateComments?: boolean` (deprecated) для совместимости со старыми записями в БД.
|
||||
- Проверить, что `computeConditionsHash` по-прежнему поддерживает legacy-снапшоты с этим полем.
|
||||
2. CI Workflow `.github/workflows/ci.yml`:
|
||||
- Обновить `node-version: 22` (текущая Active LTS).
|
||||
3. Дополнить `tests/duplicate-comments-rule.test.ts`.
|
||||
4. Verification gate & Report.
|
||||
|
|
@ -9,7 +9,6 @@ export const filterRulesSchema = z.object({
|
|||
requireRepost: z.boolean().default(false),
|
||||
requireSubscription: z.boolean().default(false),
|
||||
excludeAdmins: z.boolean().default(false),
|
||||
excludeDuplicateComments: z.boolean().optional(),
|
||||
excludeBlacklistedIds: z.array(z.string().max(128)).max(1000).default([]),
|
||||
targetGroupId: z.string().max(128).optional(),
|
||||
minEligibleParticipants: z.number().int().min(1).max(100000).default(1),
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ describe('Phase 2.2.2 Giveaway Ownership Invariant & AuthZ Guard Security Suite'
|
|||
requireSubscription: false,
|
||||
excludeAdmins: false,
|
||||
excludeBlacklistedIds: [],
|
||||
excludeDuplicateComments: true,
|
||||
minEligibleParticipants: 1,
|
||||
},
|
||||
winnersCount: 1,
|
||||
|
|
|
|||
|
|
@ -262,4 +262,60 @@ describe('Task 07: Duplicate Comments Rule & Backward Compatibility', () => {
|
|||
expect(verification.conditionsIntegrity).toBe(true);
|
||||
expect(verification.verified).toBe(true);
|
||||
});
|
||||
|
||||
// ─── 5. API Schemas Reject excludeDuplicateComments on Input ──────────────────
|
||||
it('API schemas strictly reject excludeDuplicateComments preventing it from reaching snapshot', async () => {
|
||||
const { filterRulesSchema, fetchParticipantsSchema, createSnapshotSchema, createGiveawaySchema } = await import(
|
||||
'../src/core/validation/giveaway-schemas'
|
||||
);
|
||||
|
||||
// 1. filterRulesSchema is strict and rejects the key
|
||||
const parseResult = filterRulesSchema.safeParse({
|
||||
requireLike: true,
|
||||
excludeDuplicateComments: true,
|
||||
});
|
||||
expect(parseResult.success).toBe(false);
|
||||
if (!parseResult.success) {
|
||||
expect(parseResult.error.issues.some(i => i.message.includes('unrecognized_keys') || (i as any).keys?.includes('excludeDuplicateComments'))).toBe(true);
|
||||
}
|
||||
|
||||
// 2. fetchParticipantsSchema rejects
|
||||
const fetchResult = fetchParticipantsSchema.safeParse({
|
||||
filterRules: {
|
||||
requireLike: true,
|
||||
excludeDuplicateComments: true,
|
||||
},
|
||||
});
|
||||
expect(fetchResult.success).toBe(false);
|
||||
|
||||
// 3. createSnapshotSchema rejects
|
||||
const snapResult = createSnapshotSchema.safeParse({
|
||||
filterRules: {
|
||||
requireLike: true,
|
||||
excludeDuplicateComments: true,
|
||||
},
|
||||
});
|
||||
expect(snapResult.success).toBe(false);
|
||||
|
||||
// 4. createGiveawaySchema rejects
|
||||
const createResult = createGiveawaySchema.safeParse({
|
||||
sourceUrl: 'https://vk.com/wall-1_1',
|
||||
post: {
|
||||
platform: 'VK',
|
||||
ownerId: '-1',
|
||||
postId: '1',
|
||||
sourceUrl: 'https://vk.com/wall-1_1',
|
||||
title: 'Test',
|
||||
text: 'Test',
|
||||
likesCount: 1,
|
||||
commentsCount: 0,
|
||||
repostsCount: 0,
|
||||
},
|
||||
filterRules: {
|
||||
requireLike: true,
|
||||
excludeDuplicateComments: true,
|
||||
},
|
||||
});
|
||||
expect(createResult.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ describe('Security: VK_SERVICE_TOKEN handling', () => {
|
|||
requireSubscription: false,
|
||||
excludeAdmins: false,
|
||||
excludeBlacklistedIds: [],
|
||||
excludeDuplicateComments: true,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue