Files
ipwhitelist-app/research/prompt-opus-validators.md
T

108 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Промпт для Claude Opus 4 — код-ревью validators.js
## Контекст (кратко, не анализируй — просто знай)
Делаем микросервис IP WhiteList для облачного провайдера. Клиенты управляют доверенными IPv4-адресами через веб-интерфейс. Стек: Node.js + Express + EJS + PostgreSQL.
Требования ТЗ к валидатору:
- Только IPv4, маска /22/32
- Нормализация host-битов (203.0.113.10/24 → 203.0.113.0/24), пользователь должен знать о нормализации
- Запрещены диапазоны: RFC1918 (10/8, 172.16/12, 192.168/16), CGNAT (100.64/10), Loopback (127/8), Link-local (169.254/16), IANA special (192.0.0/24), TEST-NET (192.0.2/24, 198.51.100/24, 203.0.113/24), Benchmarking (198.18/15), Multicast (224/4), Reserved (240/4), Limited broadcast (255.255.255.255/32)
- Проверка пересечений внутри компании, дубликатов, запрет вложенных подсетей
## Что нужно
Ниже код `validators.js`. Твоя задача — найти баги, уязвимости, несоответствия ТЗ и предложить исправления.
Ограничения:
- Не предлагай сменить язык/стек/фреймворк
- Не пиши «общие рекомендации» — только конкретные места с номерами строк
- Если предлагаешь исправить — напиши точный новый код
- Если багов нет — так и скажи
- **ВЕСЬ ОТВЕТ ДОЛЖЕН БЫТЬ В ОДНОМ БЛОКЕ — ОДИН markdown-блок, без интерактивных элементов, без свёрток, без ссылок на файлы. Чистый текст, готовый к копированию одной операцией.**
Файл:
```js
const net = require('net');
// Приложение А ТЗ — запрещённые диапазоны
const BLOCKED_RANGES = [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'100.64.0.0/10',
'127.0.0.0/8',
'169.254.0.0/16',
'192.0.0.0/24',
'192.0.2.0/24',
'198.51.100.0/24',
'203.0.113.0/24',
'198.18.0.0/15',
'224.0.0.0/4',
'240.0.0.0/4',
'255.255.255.255/32',
];
function validate(input) {
const raw = (input || '').trim();
if (!raw) throw new Error('Пустое значение');
if (raw.includes(':')) throw new Error('IPv6 не поддерживается');
if (/[a-zA-Z]/.test(raw.replace(/\./g, '').replace(/\//g, '').replace(/\d/g, '')))
throw new Error('Некорректный формат');
let cidr = raw.includes('/') ? raw : raw + '/32';
const [addr, maskStr] = cidr.split('/');
const mask = parseInt(maskStr, 10);
if (isNaN(mask) || mask < 22 || mask > 32) {
throw new Error('Маска должна быть от /22 до /32');
}
if (!net.isIPv4(addr)) throw new Error('Некорректный IPv4 адрес');
const ipNum = addr.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
const netMask = ~((1 << (32 - mask)) - 1) >>> 0;
const network = (ipNum & netMask) >>> 0;
const networkAddr = [
(network >>> 24) & 0xff,
(network >>> 16) & 0xff,
(network >>> 8) & 0xff,
network & 0xff,
].join('.');
const wasNormalized = addr !== networkAddr;
const normalized = networkAddr + '/' + mask;
for (const blocked of BLOCKED_RANGES) {
if (isSubnetOf(normalized, blocked)) {
throw new Error(`Диапазон ${normalized} запрещён (${blocked})`);
}
}
return { cidr: normalized, wasNormalized };
}
function overlaps(cidr1, cidr2) {
const a = cidrToRange(cidr1);
const b = cidrToRange(cidr2);
return a.start <= b.end && b.start <= a.start ||
b.start <= a.end && a.start <= b.start;
}
function isSubnetOf(cidr, parent) {
const child = cidrToRange(cidr);
const par = cidrToRange(parent);
return child.start >= par.start && child.end <= par.end;
}
function cidrToRange(cidr) {
const [addr, maskStr] = cidr.split('/');
const mask = parseInt(maskStr, 10);
const ip = addr.split('.').reduce((acc, o) => (acc << 8) + parseInt(o, 10), 0) >>> 0;
const start = ip >>> 0;
const end = (ip | ((1 << (32 - mask)) - 1)) >>> 0;
return { start, end };
}
module.exports = { validate, overlaps, BLOCKED_RANGES };
```