[CL-SEC] аудит Q2 CRITICAL#3: убрать _skipOtp из публичного requestPayout (cron → requestScheduledPayout) #784

Merged
andrei merged 1 commit from feature/claude-sec-q2-skipotp into master 2026-06-20 12:15:09 +00:00
Owner

Что сделано

CRITICAL #3 квартального аудита (SEC-Q2-2026). _skipOtp был полем data-объекта requestPayout (форма HTTP-body). Хотя Zod-схема срезает его на HTTP-входе, любой service-layer caller мог передать _skipOtp: true и обойти OTP + 24h IBAN-cooldown.

  • Bypass вынесен из data в явный internal-параметр requestPayout(..., { skipOtp }).
  • Добавлен requestScheduledPayout() — единственный санкционированный путь обхода (cron). Re-export через income-payout/index.ts + income.service.ts.
  • Cron (payoutSchedule.service.ts) переведён на requestScheduledPayout.
  • Публичный requestPayout теперь игнорирует протащенный _skipOtp → OTP/cooldown обязательны.

Зачем

Defense-in-depth: даже если внутренний код (будущий рефактор, worker) передаст _skipOtp в data-объекте, обход невозможен — bypass только через явную, greppable функцию.

План тестирования

  • Новые тесты: SEC-Q2: public requestPayout ignores smuggled _skipOtp (income-payout.test), BANK-SEC-6b (deep-payment-audit), requestScheduledPayout bypasses OTP (BANK-SEC-6).
  • vitest затронутые: 192/192. tsc --noEmit clean, eslint --max-warnings 0 clean. pre-push зелёный.

Где могу ошибаться

  • requestScheduledPayout сигнатура наследует тип data от requestPayout через Parameters<> — cron передаёт maxAmount, поле осталось в публичном типе (нужно scheduled-пути).
  • Финансовый путь (Rule C audit comment добавлен; Rule 103.1) → merge требует sign-off CEO.
  • Независим от PR #781 (разные файлы), можно мёржить в любом порядке.
## Что сделано CRITICAL #3 квартального аудита (SEC-Q2-2026). `_skipOtp` был полем `data`-объекта `requestPayout` (форма HTTP-body). Хотя Zod-схема срезает его на HTTP-входе, любой service-layer caller мог передать `_skipOtp: true` и обойти OTP + 24h IBAN-cooldown. - Bypass вынесен из `data` в явный internal-параметр `requestPayout(..., { skipOtp })`. - Добавлен `requestScheduledPayout()` — единственный санкционированный путь обхода (cron). Re-export через `income-payout/index.ts` + `income.service.ts`. - Cron (`payoutSchedule.service.ts`) переведён на `requestScheduledPayout`. - Публичный `requestPayout` теперь игнорирует протащенный `_skipOtp` → OTP/cooldown обязательны. ## Зачем Defense-in-depth: даже если внутренний код (будущий рефактор, worker) передаст `_skipOtp` в data-объекте, обход невозможен — bypass только через явную, greppable функцию. ## План тестирования - Новые тесты: `SEC-Q2: public requestPayout ignores smuggled _skipOtp` (income-payout.test), `BANK-SEC-6b` (deep-payment-audit), `requestScheduledPayout bypasses OTP` (BANK-SEC-6). - `vitest` затронутые: 192/192. `tsc --noEmit` clean, `eslint --max-warnings 0` clean. pre-push зелёный. ## Где могу ошибаться - `requestScheduledPayout` сигнатура наследует тип `data` от `requestPayout` через `Parameters<>` — cron передаёт `maxAmount`, поле осталось в публичном типе (нужно scheduled-пути). - Финансовый путь (Rule C audit comment добавлен; Rule 103.1) → merge требует sign-off CEO. - Независим от PR #781 (разные файлы), можно мёржить в любом порядке.
[CL-SEC] аудит Q2 CRITICAL#3: убрать _skipOtp из публичного requestPayout
All checks were successful
CI / Contracts (pull_request) Successful in 2m5s
CI / Telegram Mini App (pull_request) Successful in 1m38s
CI / Python SDK (pull_request) Successful in 31s
CI / Secrets Scan (pull_request) Successful in 20s
PR Janitor / janitor (pull_request) Successful in 11s
CI / Prisma Migrate Gate (pull_request) Successful in 1m43s
CI / API (pull_request) Successful in 14m56s
CI / App (pull_request) Successful in 16m45s
6df187dcd0
_skipOtp был полем data-объекта requestPayout (форма HTTP-body). Zod-схема его срезает
на HTTP-входе, но любой service-layer вызов мог передать _skipOtp:true и обойти OTP+IBAN
cooldown. Перенёс bypass в явный internal-параметр; добавил requestScheduledPayout() —
единственный санкционированный cron-путь. Публичный requestPayout теперь игнорирует
протащенный _skipOtp (тесты SEC-Q2 + BANK-SEC-6b). Cron-вызов переведён на новую функцию.

AUDIT: SEC-Q2-2026
Code-review LOW: BANK-SEC-6b полагался на default-undefined mock payoutScheduleConfig;
явно мокаем findFirst→null, чтобы reject был однозначно от OTP-гейта, не от IBAN cooldown.

AUDIT: SEC-Q2-2026
feat(admin-modernize): GrantRewardModal and CreateProposalModal refactored to shared Modal, wrapped toggles and filter states in View Transitions (Iteration 108)
All checks were successful
CI / Contracts (pull_request) Successful in 2m11s
CI / Telegram Mini App (pull_request) Successful in 1m20s
CI / Python SDK (pull_request) Successful in 22s
CI / Secrets Scan (pull_request) Successful in 13s
CI / Prisma Migrate Gate (pull_request) Successful in 1m21s
PR Janitor / janitor (pull_request) Successful in 9s
React Doctor / React Doctor / App (pull_request) Successful in 2m48s
CI / API (pull_request) Successful in 12m34s
CI / App (pull_request) Successful in 14m0s
63d6008a67
Merge branch 'master' into feature/claude-sec-q2-skipotp
All checks were successful
CI / Contracts (pull_request) Successful in 2m11s
CI / Python SDK (pull_request) Successful in 25s
CI / Secrets Scan (pull_request) Successful in 17s
CI / Telegram Mini App (pull_request) Successful in 1m20s
PR Janitor / janitor (pull_request) Successful in 8s
CI / Prisma Migrate Gate (pull_request) Successful in 1m32s
CI / API (pull_request) Successful in 13m3s
CI / App (pull_request) Successful in 15m44s
32e48bb2bb
andrei merged commit 95b6905666 into master 2026-06-20 12:15:09 +00:00
andrei deleted branch feature/claude-sec-q2-skipotp 2026-06-20 12:15:10 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
europa-tech-srl/europatech!784
No description provided.