diff --git a/tests/integration.js b/tests/integration.js
index 9d58601..368839f 100644
--- a/tests/integration.js
+++ b/tests/integration.js
@@ -516,6 +516,50 @@ async function cleanup() {
}
}
+ // ─────────────────────────────────────────────────────────────────────────────
+ // 16. Безопасность — XSS и спецсимволы в comment
+ // ─────────────────────────────────────────────────────────────────────────────
+ console.log('\n── 16. Безопасность: XSS в comment ──');
+
+ {
+ const xssPayload = '';
+ const csrf = await getCsrfFromIndex(userAgent);
+ const res = await userAgent.post('/add').type('form')
+ .send({ value: '5.5.5.5', comment: xssPayload, _csrf: csrf });
+ // Запись могла добавиться или нет (зависит от валидации comment)
+ const err = getRedirectParam(res, 'error');
+
+ // Если добавилась — проверяем что скрипт не выполняется в HTML
+ const page = await userAgent.get('/');
+ const rawScript = page.text.includes('');
+ log(!rawScript, 'security: XSS в comment — скрипт не рендерится в HTML',
+ rawScript ? 'RAW SCRIPT FOUND — XSS!' : `escaped или отклонено (err=${err})`);
+
+ // Чистим
+ await pool.query(
+ `UPDATE whitelist_entries SET deleted_at = NOW(), deleted_by = 'test-cleanup'
+ WHERE value_cidr = '5.5.5.5/32' AND deleted_at IS NULL`
+ );
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // 17. Rate-limiter — превысить mutationLimiter (30 POST/min)
+ // ─────────────────────────────────────────────────────────────────────────────
+ console.log('\n── 17. Rate-limiter ──');
+
+ {
+ // Отправляем 35 POST /add подряд с заведомо невалидным CIDR (быстро)
+ // Rate-limiter (30/min) должен сработать и вернуть 429
+ let got429 = false;
+ for (let i = 0; i < 35; i++) {
+ const csrf = await getCsrfFromIndex(userAgent);
+ const res = await userAgent.post('/add').type('form')
+ .send({ value: 'not-an-ip', comment: `rate ${i}`, _csrf: csrf });
+ if (res.status === 429) { got429 = true; break; }
+ }
+ log(got429, 'rate-limiter: POST /add → 429 после 30 запросов');
+ }
+
// ─────────────────────────────────────────────────────────────────────────────
// Cleanup
// ─────────────────────────────────────────────────────────────────────────────