Кейс · Бэкенд · TypeScript
md-safe: Markdown-парсер и санитайзер
API и веб-демо безопасного рендера Markdown в HTML на TypeScript. Парсер подмножества CommonMark написан с нуля, результат всегда проходит санитайзер по allowlist (тоже свой): готовых markdown-it, marked и dompurify в проекте нет. Проверка: корпус из 242 XSS-векторов и независимый проверяющий вывода, который не использует код санитайзера.
- TypeScript
- Node.js 24
- Markdown
- XSS
- allowlist
- fast-check
- vitest
- axe
- Docker
- GitHub Actions
01 · Как выглядит
Скриншоты


02 · О проекте
Что это и зачем
Задача: безопасно показывать пользовательский Markdown, не доверяя готовым библиотекам вслепую, и уметь доказать, что XSS не проходит.
Решение: TypeScript, парсер подмножества CommonMark и санитайзер по allowlist написаны с нуля (около 380 строк санитайзера). Проверка независима от реализации: корпус из 242 векторов и отдельный проверяющий вывода со строгим шаблоном.
Ограничения
Allowlist и тесты не заменяют аудит.
Корпус не выполнялся в реальном браузере в CI: проверяется структура вывода независимым шаблоном.
Лимиты частоты живут в памяти процесса.
Проект работает на VPS; тот же парсер работает в живом приложении scratch-notes.
Что внутри
- 242 XSS-вектора из публичных списков (OWASP XSS Filter Evasion, PortSwigger, HTML5 Security Cheatsheet): скрипты, обработчики событий, обфускация схем, data:, mXSS через svg, math, noscript, style
- Каждый вектор проходит как HTML, как Markdown с санитизацией и с экранированием, с allow_data_images и без
- Независимый проверяющий tests/safe.ts не использует код санитайзера: любой лишний тег, атрибут, схема или сырой «<» это нарушение
- fast-check: на случайных строках вывод проходит проверяющего, санитайзер идемпотентен, обычный текст не меняется
- 829 тестов; тяжёлые входы (60 тысяч «[», 40 тысяч «*a», 20 тысяч вложенных «>», 100 тысяч «<b>») быстрее 3 секунд: разбор линейный
- Результат: 200 запросов p50 9 мс, p95 31 мс, около 27 МиБ RAM
03 · Как запустить
Запуск одной командой
git clone <репозиторий по запросу> md-safe
cd md-safe
npm ci && npm test
npm run build && npm start # http://localhost:812004 · Код
Репозиторий приватный
Исходники лежат в приватном репозитории. Напишите в Telegram, открою доступ.
Запросить код