Commit Graph

8 Commits

Author SHA1 Message Date
subochev 6ee30c39fb docs(security): SEC-2/SEC-3/SEC-8 закрыты, SEC-4 усилен (updown-relayer:6)
- SEC-3 закрыт: ручка POST /close удалена, ставки закрывает сам шлюз (404)
- SEC-8 закрыт: авто-закрытие + одна открытая ставка на адрес (409)
- SEC-2 закрыт по факту: планировщик settle.ts работает, проверено живьём
- SEC-4 усилен: пара UP+DOWN больше не набирается (вторя ставка -> 409)
- SEC-5 частично: счётчик больше не накрутить массово, но /bet без авторизации
- tools/autoclose-check.cjs: сквозной живой тест (9/9 пройдено)
2026-09-13 18:49:37 +03:00
subochev 6e7132e935 docs(security): SEC-1, SEC-2, SEC-4 закрыты по сути — цена закрытия из истории
Механизм: шлюз берёт цену закрытия из локальной истории на момент expire_time
ставки (priceHistory, node:sqlite, ретеншен 24ч), а не текущую. Момент клика
перестаёт влиять на исход — окно выбора времени закрыто.

Живая проверка (ставка №10, релиз updown-relayer:3):
  цена на момент экспирации 9977000000
  текущая цена в момент клика 9976000000
  exitPrice в транзакции 9977000000  -> взята история, не 'сейчас'
  статус payout_done

Также: tools/ — скрипты проверки (price-history-probe, exploit-decisive,
exploit-check, check-close-window.sh).

Остаток: в контракте верхней границы окна по-прежнему нет (второй слой, не критично);
серверное авто-закрытие — следующая итерация.
2026-09-13 14:47:21 +03:00
subochev 4caddd6a73 docs(security): SEC-6 ЗАКРЫТ — гейт test/release + genesis, начисление вынесено из /bet
Проверено на живом стенде (релизы updown-relayer:2, test-front:3):
- /bet без address -> 400, ничего не начисляет
- /bet на непополненный кошелёк -> 400 insufficient funds
- полный круг POST /wallet -> GET /faucet -> POST /bet -> 200
- smart.mode=release -> 403 'faucet is disabled: only test mode'
- npm test 24/24, мутационная приёмка: ослабление проверки кластеров роняет 2 теста

Остаточный риск: ручка публично маршрутизируется; в бой — mode=release.
2026-09-13 13:39:09 +03:00
subochev 8ce269c16a docs(security): SEC-12 — цена не валидируется и в неcustodial-бэкенде
Проверено на smart-updown-server: GameService.submitBet не сверяет
payload.entryPrice со своим priceSource, сравнение JSON-подсказки с tx
лишь логирует log.warn и не отклоняет. Клиент подписывает entry_price сам.

Зафиксирована инверсия: custodia-шлюз стенда эту дыру закрывает (цена из
NATS на сервере), а неcustodial прод-бэкенд открыт. Переход на неcustodial
без серверной сверки entry_price ухудшит положение.
2026-09-13 13:11:00 +03:00
subochev 5b47de0870 docs(security): SEC-6 — назначение ручки vs гарантия её отсутствия на проде
Зафиксировано разделение: ручка тестовая по назначению (faucet-TASK.md) и на
проде не нужна, однако проверка сети в коде отсутствует, а дефолт
SMART_ALLOW_AIRDROP='1' (config.ts) — открыт. Значит 'не нужна' держится
памятью деплойщика, а не кодом. Фикс: дефолт '0' + getGenesisHash() в handleFaucet.
2026-09-13 12:54:08 +03:00
subochev 08ea780f7d docs(security): SEC-6 — уточнено, что фантиковая часть не localnet-only
Разделены два механизма ручки /faucet:
(а) SOL-налив (requestAirdrop) — технически только localnet, mainnet-RPC
    отдаёт 410 (проверено вживую);
(б) налив фантиков (mintTo) — НЕ привязан к сети, гейт только конфиг-флаг
    SMART_ALLOW_AIRDROP, проверки кластера в коде нет;
    SOL-барьер обходится: при балансе >=2 SOL airdrop не зовётся (ops.ts:204)
    и выполнение доходит прямо до mintTo.
Отмечено, что на боевом мемкойне pump.fun (revoked mint authority) часть (б)
исчезает сама.
2026-09-13 12:46:11 +03:00
subochev 1d49468920 docs(security): нумерация пунктов чек-листа SEC-1..SEC-15
Сквозные номера не сбиваются при добавлении/закрытии пунктов внутри
разделов. В SEC-4 фикс ссылается на SEC-1/SEC-2. В сводке — раскладка
по важности.
2026-09-13 12:42:27 +03:00
subochev 18ecbb42b3 docs(security): чек-лист безопасности игрового контура (аудит живого стенда)
15 пунктов, отмечаются [ ]/[x]. Критичное найдено воспроизведением на
testfront.binom.pw, не чтением кода:
- close_bet проверяет только нижнюю границу окна (now >= expire_time) —
  верхней нет, ставка висит open бесконечно, момент расчёта выбирает
  вызывающий /close;
- в шлюзе нет авто-закрытия (единственный setInterval — WS-heartbeat);
- /bet, /close, /faucet открыты наружу без авторизации; faucet выдал
  5 SOL + 10000 фантиков при SMART_ALLOW_AIRDROP=1 в боевом поде;
- эксплойт воспроизведён: ставка DOWN (entry=9998000000) висела open >2 мин
  после экспирации 15с и закрыта при exitPrice=9997000000 -> payout_done.
Плюс: множитель 1.3x требует 77% винрейта, запертые средства в ваулте,
равные цены не финализируют ставку, NATS без auth, релейер — точка доверия.
2026-09-13 12:40:51 +03:00