docs(security): SEC-6 — назначение ручки vs гарантия её отсутствия на проде
Зафиксировано разделение: ручка тестовая по назначению (faucet-TASK.md) и на проде не нужна, однако проверка сети в коде отсутствует, а дефолт SMART_ALLOW_AIRDROP='1' (config.ts) — открыт. Значит 'не нужна' держится памятью деплойщика, а не кодом. Фикс: дефолт '0' + getGenesisHash() в handleFaucet.
This commit is contained in:
+2
-1
@@ -22,7 +22,8 @@
|
||||
- [ ] **SEC-3. `POST /close` доступен из интернета без авторизации** — любой может закрыть чужую ставку в выбранный им момент. Проверено: `curl -X POST https://testfront.binom.pw/api/close -d '{"id":"1"}'` → обработчик выполняется. Фикс: авторизация + серверное закрытие.
|
||||
- [ ] **SEC-4. Безрисковый арбитраж на паре ставок UP+DOWN** — при `entry ≈ X` обе ставки закрываются в свой выгодный момент, обе получают `payout_done` (пара как схема — **вывод** из дыры окна; прямого прогона пары не делали). Прямо воспроизведено: ставка №5 (`DOWN`, `entry=9998000000`) висела `open` >2 мин после экспирации 15 с и закрыта при `exitPrice=9997000000` → `{"status":1,"statusName":"payout_done"}` — момент расчёта выбран вручную. Фикс: `SEC-1` + `SEC-2`.
|
||||
- [ ] **SEC-5. `POST /bet` без авторизации** — любой может создавать ставки на произвольные адреса и накручивать счётчик раундов `global.counter`. Проверено: `curl -X POST .../api/bet` → 200. Фикс: токен/подпись запроса.
|
||||
- [ ] **SEC-6. `GET /faucet/{address}` открыт наружу и выдаёт реальные средства** — из двух механизмов внутри ручки (`handleFaucet`, `gateway.ts:395`): **(а) SOL-налив** (`fundBettor` → `requestAirdrop`) технически только localnet — mainnet-RPC отвечает `410 Gone "RPC call or parameters have been disabled"` (проверено вживую); **(б) налив фантиков** (`ensureBettorTokenAccount` → `mintTo`) — **НЕ привязан к сети**: работает везде, где у шлюза есть mint authority нашего минта (наш `8dpXNUgW…` — authority `E7Hg…` у релейера, проверено). Гейт — только конфиг-флаг `SMART_ALLOW_AIRDROP`, **проверки кластера в коде нет** (текст «faucet only on localnet» — надпись, не замок). Дополнительно: SOL-барьер обходится — airdrop зовётся лишь при балансе <2 SOL (`ops.ts:204`), значит адрес с ≥2 SOL пролетает airdrop и доходит прямо до `mintTo`. Проверено на стенде: `curl …/api/faucet/6Q1ko…` → `{"sol":5,"solLamports":5000000000,"tokenUnits":"10000000000"}`. Фикс: `SMART_ALLOW_AIRDROP=0` вне localnet (per-environment values), ручку не публиковать наружу. **На боевом мемкойне pump.fun дыра по (б) испаряется сама** — у таких токенов mint authority отзывают; но пока на стенде свой минт с нашей authority — она живая, и полагаться на это нельзя.
|
||||
- [ ] **SEC-6. `GET /faucet/{address}` открыт наружу и выдаёт реальные средства** — из двух механизмов внутри ручки (`handleFaucet`, `gateway.ts:395`): **(а) SOL-налив** (`fundBettor` → `requestAirdrop`) технически только localnet — mainnet-RPC отвечает `410 Gone "RPC call or parameters have been disabled"` (проверено вживую); **(б) налив фантиков** (`ensureBettorTokenAccount` → `mintTo`) — **НЕ привязан к сети**: работает везде, где у шлюза есть mint authority нашего минта (наш `8dpXNUgW…` — authority `E7Hg…` у релейера, проверено). Гейт — только конфиг-флаг `SMART_ALLOW_AIRDROP`, **проверки кластера в коде нет** (текст «faucet only on localnet» — надпись, не замок). Дополнительно: SOL-барьер обходится — airdrop зовётся лишь при балансе <2 SOL (`ops.ts:204`), значит адрес с ≥2 SOL пролетает airdrop и доходит прямо до `mintTo`. Проверено на стенде: `curl …/api/faucet/6Q1ko…` → `{"sol":5,"solLamports":5000000000,"tokenUnits":"10000000000"}`. Фикс: `SMART_ALLOW_AIRDROP=0` вне localnet (per-environment values), **дефолт в `config.ts` перевернуть на `"0"`** (сейчас `env("SMART_ALLOW_AIRDROP","1")` — открыто по умолчанию), добавить в `handleFaucet` проверку кластера (genesis hash / `getGenesisHash()`), ручку не публиковать наружу. **На боевом мемкойне pump.fun дыра по (б) испаряется сама** — у таких токенов mint authority отзывают; но пока на стенде свой минт с нашей authority — она живая, и полагаться на это нельзя.
|
||||
*Назначение vs гарантия:* ручка задумана как тестовая (ТЗ `relayer/faucet-TASK.md`: «чтобы Phantom-кошелёк показал баланс > 0») и **на проде не нужна** — там деньги покупаются через swap, а `/bet` фандит беттора сам (`gateway.ts:268-278`), так что даже для тестов ручка почти избыточна (нужна лишь для внешнего Phantom на localnet; `test-front` её не вызывает). Но «не нужна» ≠ «не сработает»: держит её **память деплойщика**, а не код. Наличие ручки в боевом релизе при `allowAirdrop: true` (`helm/values.yaml:22`) означает **работающий публичный кран**, а не безобидный легаси.
|
||||
|
||||
## Важно — экономика и устойчивость
|
||||
|
||||
|
||||
Reference in New Issue
Block a user