research: ответ Opus — баг isSubnetOf в блокировке, фикс промпта
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
# Ответ Claude Opus 4 — код-ревью validators.js
|
||||||
|
|
||||||
|
> Дата: 2026-05-30
|
||||||
|
|
||||||
|
## 🔴 Баг 1 — запрещённые диапазоны обходятся бо́льшей подсетью (критично)
|
||||||
|
|
||||||
|
Проверка блокировки использует `isSubnetOf(normalized, blocked)` — то есть «запись внутри запрещённого диапазона». Но не проверяется обратное направление: **запись, которая САМА содержит запрещённый диапазон**.
|
||||||
|
|
||||||
|
Поскольку ТЗ разрешает маски вплоть до `/22`, а многие запрещённые блоки — это `/24`, их можно обойти суперсетью:
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: 192.0.2.0/22
|
||||||
|
→ нормализуется в 192.0.0.0/22 (диапазон 192.0.0.0 – 192.0.3.255)
|
||||||
|
```
|
||||||
|
|
||||||
|
Этот `/22` **содержит** TEST-NET-1 (`192.0.2.0/24`) и IANA special (`192.0.0.0/24`), но `isSubnetOf` вернёт `false` (родитель не внутри ребёнка) → запись пройдёт валидацию. То же с `198.18.0.0/15` (Benchmarking) и др.
|
||||||
|
|
||||||
|
**Фикс** — использовать пересечение вместо вложенности:
|
||||||
|
|
||||||
|
```js
|
||||||
|
for (const blocked of BLOCKED_RANGES) {
|
||||||
|
if (overlaps(normalized, blocked)) {
|
||||||
|
throw new Error(`Диапазон ${normalized} пересекается с запрещённым (${blocked})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`overlaps` ловит оба направления (и когда запись внутри блока, и когда блок внутри записи). `isSubnetOf` после этого фикса в блокировке не нужен.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 Замечание 2 — parseInt глотает мусор в маске
|
||||||
|
|
||||||
|
```js
|
||||||
|
const mask = parseInt(maskStr, 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
`10.0.0.0/24abc` → `parseInt('24abc')` = `24`, запись принимается как `/24`. Строгая проверка:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (!/^\d{1,2}$/.test(maskStr)) throw new Error('Некорректная маска');
|
||||||
|
const mask = parseInt(maskStr, 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 Замечание 3 — множественные слэши не отсекаются
|
||||||
|
|
||||||
|
`10.0.0.0/24/8`.split('/') → `['10.0.0.0','24','8']`, третий элемент молча игнорируется. Добавить проверку числа частей:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const parts = cidr.split('/');
|
||||||
|
if (parts.length !== 2) throw new Error('Некорректный формат CIDR');
|
||||||
|
const [addr, maskStr] = parts;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Не баги (проверено)
|
||||||
|
|
||||||
|
- **`overlaps`** — формула `a.start <= b.end && b.start <= a.start || b.start <= a.end && a.start <= b.start` хоть и обфусцирована, **математически эквивалентна** стандартной `a.start <= b.end && b.start <= a.end`. Работает корректно. Можно упростить для читаемости, но это не дефект.
|
||||||
|
- **Битовая арифметика** с `<<8` уходит в знаковый 32-бит, но финальный `>>> 0` всё исправляет. Корректно.
|
||||||
|
- **Маска `/0`**, `1 << (32-mask)` — безопасно, т.к. `mask` ограничен 22–32.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Итог:** один настоящий эксплуатируемый баг (#1 — обход TEST-NET/special через `/22`), два мелких по строгости парсинга. Главное — поправить блокировку на `overlaps`.
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
- Не пиши «общие рекомендации» — только конкретные места с номерами строк
|
- Не пиши «общие рекомендации» — только конкретные места с номерами строк
|
||||||
- Если предлагаешь исправить — напиши точный новый код
|
- Если предлагаешь исправить — напиши точный новый код
|
||||||
- Если багов нет — так и скажи
|
- Если багов нет — так и скажи
|
||||||
|
- **ВЕСЬ ОТВЕТ ДОЛЖЕН БЫТЬ В ОДНОМ БЛОКЕ — ОДИН markdown-блок, без интерактивных элементов, без свёрток, без ссылок на файлы. Чистый текст, готовый к копированию одной операцией.**
|
||||||
|
|
||||||
Файл:
|
Файл:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user