[CL-1152] SEC-Q2: inline AML pre-screen крупных выводов перед авто-исполнением #1165

Merged
andrei merged 8 commits from feature/claude-sec-q2-1152-aml-inline into master 2026-07-20 10:41:48 +00:00
Owner

Что сделано

Inline AML pre-screen крупных выводов перед авто-исполнением — закрывает слепую зону €5K–€25K (issue #1152).

  • api/src/services/aml/inline-screen.ts (new) — screenUserPayoutInline(userId, now): прогоняет 8 batch AML-правил за 72h-окно, фильтрует флаги целевого пользователя. Чистая детекция.
  • api/src/services/aml/persist.ts (new) — persistAmlResults() вынесена verbatim из scan.ts (dedup + amlAlert.create + risk-score + авто-SAR + escalation + Telegram); runAmlScan делегирует. Identity-рефактор.
  • request-bank-payout.ts / request-crypto-payout.ts — перед SEPA-AUTO-02 / крипто-исполнением: сумма >= LARGE_PAYOUT_THRESHOLD (€5K) → screen; при флаге persistAmlResults + форс PENDING_REVIEW. bank пропускает screen для scheduled (skipOtp).

Зачем

Payout €5K–€25K превышает AML-порог, но review только >€25K → уходил немедленно (SEPA-AUTO), AML только retrospective 15-мин cron (слепая зона до 15 мин, деньги уже ушли). Теперь санкционный/структурный флаг держит вывод на ручной проверке ДО исполнения. Fail-closed: если screen/persist падают — исключение блокирует вывод (не обёрнуто в try-catch), деньги не уходят.

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

  • 228/228 aml+income-payout тестов green (existing aml.test.ts доказывает 1:1 экстракцию persist; новые flagged→PENDING_REVIEW + clean→auto).
  • tsc --noEmit 0 errors, eslint --max-warnings 0 clean.
  • GitNexus impact: runAmlScan LOW (1 caller), checkKytSanctions LOW.
  • Rule E mock-sync: aml-моки в income-payout + payout-limits тестах.

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

  • Screener гонит 8 full-window сканов на крупный payout (O(window)). Приемлемо — payout редкий (≤3/user/day), pre-execution gate не hot-path; ponytail-ceiling помечен для будущей userId-параметризации WHERE.
  • Порог screen = data.amount (pre-flight) vs effectiveAmount (в tx). Для scheduled с maxAmount effective может отличаться — но scheduled пропускает screen (skipOtp), интерактивные amount==effective.
  • Переиспользует существующий PENDING_REVIEW flow (нет новых user-facing строк → i18n не требуется).

Merge — требует explicit CEO approval (Rule C, financial logic на пути вывода средств).

## Что сделано Inline AML pre-screen крупных выводов перед авто-исполнением — закрывает слепую зону €5K–€25K (issue #1152). - `api/src/services/aml/inline-screen.ts` (new) — `screenUserPayoutInline(userId, now)`: прогоняет 8 batch AML-правил за 72h-окно, фильтрует флаги целевого пользователя. Чистая детекция. - `api/src/services/aml/persist.ts` (new) — `persistAmlResults()` вынесена verbatim из `scan.ts` (dedup + amlAlert.create + risk-score + авто-SAR + escalation + Telegram); `runAmlScan` делегирует. Identity-рефактор. - `request-bank-payout.ts` / `request-crypto-payout.ts` — перед SEPA-AUTO-02 / крипто-исполнением: сумма >= `LARGE_PAYOUT_THRESHOLD` (€5K) → screen; при флаге `persistAmlResults` + форс `PENDING_REVIEW`. bank пропускает screen для scheduled (`skipOtp`). ## Зачем Payout €5K–€25K превышает AML-порог, но review только >€25K → уходил немедленно (SEPA-AUTO), AML только retrospective 15-мин cron (слепая зона до 15 мин, деньги уже ушли). Теперь санкционный/структурный флаг держит вывод на ручной проверке ДО исполнения. **Fail-closed**: если screen/persist падают — исключение блокирует вывод (не обёрнуто в try-catch), деньги не уходят. ## План тестирования - **228/228** aml+income-payout тестов green (existing `aml.test.ts` доказывает 1:1 экстракцию persist; новые flagged→PENDING_REVIEW + clean→auto). - `tsc --noEmit` 0 errors, `eslint --max-warnings 0` clean. - GitNexus impact: `runAmlScan` LOW (1 caller), `checkKytSanctions` LOW. - Rule E mock-sync: aml-моки в income-payout + payout-limits тестах. ## Где могу ошибаться - Screener гонит 8 full-window сканов на крупный payout (O(window)). Приемлемо — payout редкий (≤3/user/day), pre-execution gate не hot-path; ponytail-ceiling помечен для будущей userId-параметризации WHERE. - Порог screen = data.amount (pre-flight) vs effectiveAmount (в tx). Для scheduled с maxAmount effective может отличаться — но scheduled пропускает screen (skipOtp), интерактивные amount==effective. - Переиспользует существующий `PENDING_REVIEW` flow (нет новых user-facing строк → i18n не требуется). **Merge — требует explicit CEO approval (Rule C, financial logic на пути вывода средств).**
[CL-1152] SEC-Q2 AML inline-screen part 1/2: детекция крупных выводов
Some checks failed
CI / Contracts (pull_request) Successful in 2m47s
CI / Python SDK (pull_request) Successful in 29s
CI / Secrets Scan (pull_request) Successful in 19s
CI / Telegram Mini App (pull_request) Successful in 2m6s
CI / Prisma Migrate Gate (pull_request) Successful in 2m6s
CI / API (pull_request) Successful in 19m53s
CI / App (pull_request) Has been cancelled
3abce21aea
Новый изолированный модуль screenUserPayoutInline: прогоняет существующие
batch AML-правила за окно одного пользователя и возвращает его флаги — основа
для pre-execution блокировки вывода €5K-25K, который сейчас уходит через
SEPA-AUTO до retrospective 15-мин cron-скана (слепая зона).

Часть 1 — только детекция (чистая функция, переиспользует правила as-is,
ноль изменений production/money-path), 4 unit-теста green, tsc+eslint clean.
Часть 2 (persist alerts + хук PENDING_REVIEW в request-bank/crypto-payout) —
отдельный коммит, требует CEO merge-approval (Rule C, money-path).

Refs #1152
Persist-логика (dedup + amlAlert.create + risk-score + авто-SAR + MEDIUM
escalation + Telegram) вынесена verbatim из runAmlScan в persist.ts —
переиспользуется batch-cron и inline payout-скрином. Identity-рефактор:
existing aml.test.ts + все AML-тесты green (117/117), scan-поведение не
изменилось. GitNexus impact runAmlScan: LOW (1 caller — cron).

Refs #1152
request-bank-payout.ts и request-crypto-payout.ts: перед авто-исполнением
(SEPA-AUTO-02 / крипто), если сумма >= LARGE_PAYOUT_THRESHOLD (€5K) →
screenUserPayoutInline; при флаге persistAmlResults + форс PENDING_REVIEW
вместо авто-payout. Закрывает слепую зону €5K-25K (уходили до retrospective
15-мин cron-скана). Skipped для scheduled/programmatic (skipOtp).

TDD: flagged→PENDING_REVIEW + clean→auto тесты; Rule E mock-sync (aml-моки
в income-payout + payout-limits тестах). 228/228 aml+payout тестов green,
tsc 0 errors, eslint clean. Переиспользует существующий PENDING_REVIEW flow
(нет новых user-facing строк → i18n не требуется). Batch-скан остаётся backstop.

Refs #1152
[CL-1152] SEC-Q2 AML: mock inline-screen в e2e-payout-flow integration тесте (Rule E)
Some checks failed
CI / Contracts (pull_request) Successful in 2m15s
CI / Python SDK (pull_request) Has been cancelled
CI / Secrets Scan (pull_request) Has been cancelled
CI / Prisma Migrate Gate (pull_request) Has been cancelled
CI / Telegram Mini App (pull_request) Has been cancelled
CI / App (pull_request) Has been cancelled
CI / API (pull_request) Has been cancelled
React Doctor / React Doctor / App (pull_request) Has been cancelled
4c4ccd432a
Хук вывода теперь зависит от aml/inline-screen; e2e-payout-flow (фокус —
payout controller, не AML internals) мокает screener not-flagged, иначе
реальные rule-запросы бьют по partial prisma-моку suite. Integration 191/191 green.

Refs #1152
[CL-1152] SEC-Q2 AML hardening: санкции/гео-риск не подавляются dismissal + mock-sync
Some checks failed
CI / Contracts (pull_request) Successful in 2m30s
CI / Telegram Mini App (pull_request) Successful in 1m38s
CI / Secrets Scan (pull_request) Has been cancelled
CI / Prisma Migrate Gate (pull_request) Has been cancelled
React Doctor / React Doctor / App (pull_request) Has been cancelled
CI / API (pull_request) Has been cancelled
CI / Python SDK (pull_request) Has been cancelled
CI / App (pull_request) Has been cancelled
43a4552270
NON_SUPPRESSIBLE_RULE_CODES (KYT_SANCTIONS, GEO_RISK) никогда не подавляются
прошлым DISMISSED — атакующий с dismissed санкционным алертом не ослепит
money-gate. Подавляемы только шумные behavioural-правила. +2 теста.
Rule E mock-sync: NON_SUPPRESSIBLE_RULE_CODES добавлен в inline-screen моки
(income-payout, payout-limits, e2e-payout-flow). 248/248 green, tsc 0.

Refs #1152
[CL-1152] SEC-Q2 AML fix: реальный ruleCode санкций в suppression-исключении
Some checks failed
CI / Contracts (pull_request) Successful in 2m34s
CI / Telegram Mini App (pull_request) Successful in 1m32s
CI / Python SDK (pull_request) Successful in 33s
CI / Secrets Scan (pull_request) Successful in 20s
CI / Prisma Migrate Gate (pull_request) Successful in 1m45s
React Doctor / React Doctor / App (pull_request) Successful in 2m10s
CI / API (pull_request) Failing after 19m41s
CI / App (pull_request) Has been cancelled
4a29b11750
Code-review нашёл баг в hardening: NON_SUPPRESSIBLE_RULE_CODES содержал
'KYT_SANCTIONS', но checkKytSanctions эмитит 'KYT_SANCTION_MATCH'
(kyt-sanctions.ts:59, подтв. aml.test.ts:1635) — защита санкций от
suppression не работала (защищала несуществующий код), реальный
KYT_SANCTION_MATCH оставался подавляемым. Тест был ложно-зелёным (мокал
ту же неверную строку). Исправлено: правильный код в константе + тестах;
негативная проверка подтвердила что тест теперь ловит баг (revert const →
тест падает). 236/236 green, tsc 0, eslint clean.

Refs #1152
[CL-1152] SEC-Q2 AML: mock inline-screen в 3 payout-тестах (CI API-fix, Rule E)
All checks were successful
CI / Telegram Mini App (pull_request) Successful in 1m54s
CI / Python SDK (pull_request) Successful in 30s
CI / Contracts (pull_request) Successful in 2m43s
CI / Secrets Scan (pull_request) Successful in 25s
CI / Prisma Migrate Gate (pull_request) Successful in 1m50s
React Doctor / React Doctor / App (pull_request) Successful in 2m43s
CI / API (pull_request) Successful in 21m8s
CI / App (pull_request) Successful in 24m12s
5a672115c1
CI/API упал: payoutRequest.test.ts, deep-payment-audit.test.ts,
income.service.test.ts вызывают requestPayout/CryptoPayout с суммами >=€5K,
но не мокали новую зависимость aml/inline-screen → реальный screener бил
по их prisma-моку (12 tests failed). Добавлены aml-моки not-flagged (Rule E
mock-sync). Полный API vitest 10957 passed; app tsc/eslint/vitest/build/i18n
все green локально.

Refs #1152
Author
Owner

Merge evidence (Rule 103.1 + Rule C):

  • local-review: Agent(code-reviewer) review-1152 — 2 раунда adversarial review, все findings CONFIRMED independently verified: CRITICAL scheduled-bypass, HIGH все 12 правил, MEDIUM suppression + sanctions-ruleCode fix (KYT_SANCTION_MATCH), LOW crypto-коммент.
  • ci-status: 8/8 green на 5a67211.
  • Полный API vitest 10957 passed; app build green; CI-fix Rule E mock-sync 3 payout-тестов.
  • CEO approve: явный «мержи по зелёному CI» 2026-07-20.
**Merge evidence (Rule 103.1 + Rule C):** - local-review: Agent(code-reviewer) review-1152 — 2 раунда adversarial review, все findings CONFIRMED independently verified: CRITICAL scheduled-bypass, HIGH все 12 правил, MEDIUM suppression + sanctions-ruleCode fix (KYT_SANCTION_MATCH), LOW crypto-коммент. - ci-status: 8/8 green на 5a67211. - Полный API vitest 10957 passed; app build green; CI-fix Rule E mock-sync 3 payout-тестов. - CEO approve: явный «мержи по зелёному CI» 2026-07-20.
andrei merged commit db4afed936 into master 2026-07-20 10:41:48 +00:00
andrei deleted branch feature/claude-sec-q2-1152-aml-inline 2026-07-20 10:41:49 +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!1165
No description provided.