WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-290 · Дефект · Вход · web

SECURITY: insensitive email-поиск = ILIKE без экранирования — символ _ работает как wildcard и матчит ЧУЖОЙ аккаунт

Закрыт P1 · важно ведёт: —
Суть
Найдено координатором 17.08 живым грантом: POST /api/admin/wallet-grant с email=_@wool2.online вернул targetEmail=3@wool2.online — Prisma findFirst({email:{equals,mode:insensitive}}) на Postgres реализуется через ILIKE, где _ = одиночный wildcard, и findFirst взял ПЕРВОГО попавшегося (3@wool2.online) вместо точного _@wool2.online (оба существуют). Последствия: (1) грант ушёл не тому (.01 на 3@wool2.online, ledger cmsw85cu…/cmsw8cy1t — вернуть/списать при разборе); (2) КЛАСС УЯЗВИМОСТИ: любой lookup по email с mode:insensitive матчит чужие аккаунты через _ и % в email (логин? восстановление пароля? инвайты?) — нужен АУДИТ ВСЕХ мест insensitive-поиска email и экранирование/citext/lower()=lower(). Обход для гранта: userId (сработал).

## 2026-08-23 11:30 UTC — фикс-волна запущена (A1 web290, Terra high)
Бриф /home/ubuntu/waves/WEB290-BRIEF.md: аудит ВСЕХ insensitive-email мест, классовый хелпер lower=lower без wildcard-семантики, НЕГАТИВЫ (_ и % не матчат чужое, регистр сохранён), без миграций. Ожидаю WEB290_FIX_READY. Кандидат посадки 33.


## 2026-08-23 12:20 UTC — отчёт сдан, но сдача НЕ принята (патч отсутствует, OOM в логе)
WEB290-FIX-REPORT.md появился: аудит хороший (все email-lookup пути в таблице; в baseline НЕТ prod mode:insensitive на User.email — инцидент-эра, видимо, была уже частично закрыта), фикс = exact-CI хелпер lower("email")=lower(?) c Prisma-bind (wildcard = литерал), 21 mock-тест с негативами (_ не матчит, % не раскрывается, регистр ок). НО: web290-fix.patch НЕ создан, коммита в wt-web290 НЕТ (файл изменён uncommitted), а хвост web290-codex.log = node FatalProcessOutOfMemory, exit 134 — вероятно pnpm build столкнулся памятью с параллельной web063. По правилу cover-claims-only-what-ships-with-receipts сдача не принимается до появления коммита+патча. Сессия codex жива — жду рекавери; иначе салваж координатором (закоммитить worktree, прогнать tsx-тесты, экспортировать патч).
УРОК: не гонять 2+ pnpm build параллельно на A1 (24G) — билд-шаги волн разносить.


## 2026-08-23 12:35 UTC — патч ДОЕХАЛ (рекавери после OOM), принят с оговоркой
Codex дорекаверился: web290-fix.patch экспортирован (8048 B, sha ec147e2b…, копия на Интеле). Коммита в wt-web290 нет (диф uncommitted) — принимаю ПАТЧ как сдачу с оговоркой: mock-тесты (21, включая негативы _/%) прогонялись ДО OOM; при сборке посадки 33 патч ляжет на релизную ветку и tsx-тесты+tsc прогоняются заново (обязательный шаг перед судом). Кандидат посадки 33.


## 2026-08-23 14:57 UTC — ЗАДЕПЛОЕНО: посадка 33 (v4-00c402c) села с 1-й попытки
rel-00c402c (SHA 58af4a94…), аттестованный cutover c НОВЫМ пин-гейтом (все 7 env-пинов сверены с аттестацией ДО флипа — негативный гейт из урока посадки 32). paidReady=true, канон проштампован. Живая приёмка фикса — следующий шаг.


## 2026-08-23 BOARDSWEEP: STALE-NOT-DEPLOYED
- Evidence: buildFixed is empty. Canonical wallet/admin search paths were inspected; no deployed build pointer binds wildcard-safe insensitive lookup to v4-081ff4fb5.
- buildFixed: <empty>; canonical: v4-081ff4fb5 / 081ff4fb5f6110f8001daec7ed910ccef2e395dc.
- Status preserved by sweep: review.
Доказательства

[2026-08-24 STALEREV против 61bc04462; в проде с посадки 45 v4-5a4edbac3] Вердикт SERVED: реализация+тесты доказаны в target tree; принято по приёмке 45-тикетного разбора. Отчёт: A1 /home/ubuntu/waves/STALEREVIEW-REPORT.md (STALEREVIEW_DONE).
[2026-08-26 00:55Z] Автор web290: email ILIKE экранирован (_ % не wildcard, точное сравнение), enumeration через _/% отклонена. -> review.
[2026-08-26 01:10Z] ПРИНЯТ acc290: PASS — негативы _/%/\ (не wildcard), exact match работает, wallet-write блокировка. -> done.
Починено в
a453b16
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-290","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-26T01:06:58.081Z