Санитизация в библиотеке Validator.js рассматривается как процесс
приведения входных строк к безопасному и предсказуемому виду перед
дальнейшей обработкой. В отличие от валидации, которая отвечает за
проверку корректности данных, санитизация изменяет вход, устраняя
потенциально опасные или нежелательные символы и конструкции. В
Validator.js для этого используются такие методы, как
escape, trim, stripLow,
normalizeEmail, а также ряд вспомогательных функций,
применяемых к строкам.
Основная особенность тестирования санитизации заключается в том, что проверяется не факт соответствия правилам, а трансформация входных данных. Это требует более детального подхода к фиксации ожидаемых преобразований, особенно в пограничных случаях.
Санитизаторы в Validator.js обладают детерминированным поведением: одинаковый вход всегда приводит к одинаковому выходу. Это свойство делает их удобными для модульного тестирования. Однако сложность возникает при обработке:
Каждая из этих категорий требует отдельного набора тестов, поскольку изменения в поведении санитизатора часто приводят к регрессиям безопасности.
escape и защита от XSSМетод escape предназначен для экранирования
HTML-специфичных символов:
< → <> → >& → &" → "' → 'При тестировании важно учитывать не только прямую замену символов, но и комбинированные случаи.
Пример тестового сценария:
import validator from "validator";
test("escape должен экранировать HTML символы", () => {
const input = `<script>alert("XSS")</script>`;
const output = validator.escape(input);
expect(output).toBe(
`<script>alert("XSS")</script>`
);
});
Особое внимание уделяется цепочкам вложенных конструкций:
test("escape корректно обрабатывает уже экранированные сущности", () => {
const input = `<b>text</b>`;
const output = validator.escape(input);
expect(output).toBe(`&lt;b&gt;text&lt;/b&gt;`);
});
Такой тест выявляет проблему двойного экранирования, которая может возникать при многократной обработке данных.
trim и работа с пробельными символамиФункция trim удаляет пробелы по краям строки, но
тестирование выходит за пределы обычных ASCII-пробелов. Важны следующие
категории:
\t);\n, \r\n);\u00A0);Пример тестов:
test("trim удаляет пробелы по краям строки", () => {
expect(validator.trim(" hello ")).toBe("hello");
});
test("trim обрабатывает табы и переносы строк", () => {
expect(validator.trim("\n\t hello \r\n")).toBe("hello");
});
Отдельное значение имеет тестирование строк, содержащих только пробельные символы, поскольку результатом должна быть пустая строка.
stripLow
и обработка управляющих символовМетод stripLow удаляет ASCII-контрольные символы (коды
< 32 и 127). Тестирование требует проверки диапазонов значений:
test("stripLow удаляет управляющие символы", () => {
const input = "hello\u0000world\u0008";
const output = validator.stripLow(input);
expect(output).toBe("helloworld");
});
Важным аспектом является режим сохранения табуляций и переносов строк, который может быть включён параметрами. Это требует параметризованных тестов:
test("stripLow сохраняет переносы строк при включенной опции", () => {
const input = "a\nb\rc";
const output = validator.stripLow(input, { keep_new_lines: true });
expect(output).toBe("a\nb\rc");
});
normalizeEmail
и сложные правила преобразованияСанитизация email-адресов относится к наиболее сложным случаям. Метод
normalizeEmail изменяет домены и локальные части адреса в
зависимости от правил почтовых провайдеров.
Основные направления тестирования:
Пример:
test("normalizeEmail приводит Gmail к каноническому виду", () => {
const input = "Test.Email+spam@GMAIL.com";
const output = validator.normalizeEmail(input);
expect(output).toBe("testemail@gmail.com");
});
Также важно тестировать отключаемые параметры:
test("normalizeEmail сохраняет точки при отключенной опции gmail_remove_dots", () => {
const input = "t.e.s.t@gmail.com";
const output = validator.normalizeEmail(input, {
gmail_remove_dots: false
});
expect(output).toBe("t.e.s.t@gmail.com");
});
Санитизация часто ломается на неожиданных входах:
null и undefined (при нестрогой
типизации);Пример теста на устойчивость:
test("sanitize не ломается на пустой строке", () => {
expect(validator.escape("")).toBe("");
});
И тест на длинные входы:
test("escape корректно обрабатывает длинные строки", () => {
const input = "a".repeat(10000);
const output = validator.escape(input);
expect(output.length).toBe(10000);
});
Для Validator.js эффективно применяется property-based подход, при котором проверяются свойства функций, а не конкретные примеры.
Основные свойства:
escape никогда не содержит необработанных
< или >;trim не увеличивает длину строки;stripLow не добавляет символы;Пример свойства:
test("escape не оставляет символов < и >", () => {
fc.assert(
fc.property(fc.string(), (str) => {
const result = validator.escape(str);
return !result.includes("<") && !result.includes(">");
})
);
});
При обновлении Validator.js критично фиксировать старое поведение функций. Любое изменение в алгоритмах санитизации может привести к:
Регрессионные тесты фиксируют конкретные входы и ожидаемые выходы, предотвращая непреднамеренные изменения:
test("регрессия escape для кавычек", () => {
expect(validator.escape("\"'")).toBe(""'");
});
В реальных приложениях санитизация редко используется изолированно. Чаще она комбинируется:
const result = validator.trim(
validator.escape(userInput)
);
Тестирование таких цепочек требует проверки промежуточных и конечных состояний. Важно учитывать порядок вызовов, поскольку он влияет на итоговый результат.
Основная цель тестирования санитизации заключается в предотвращении классов уязвимостей:
Тесты должны моделировать реальные атаки, включая комбинированные payload-строки:
test("escape блокирует XSS payload", () => {
const input = `<img src=x oner ror=alert(1)>`;
const output = validator.escape(input);
expect(output.includes("onerror")).toBe(false);
});