Whitelist-подход

Whitelist-подход в контексте библиотеки Marked подразумевает строгий контроль над тем, какие элементы Markdown разрешены к обработке и преобразованию в HTML. Это ключевой механизм безопасности, предотвращающий внедрение нежелательного или опасного кода, включая XSS-атаки.

В Marked настройка whitelist достигается через конфигурацию параметров renderer и sanitize, а также через собственные фильтры токенов и тегов.


Настройка безопасного рендеринга

Marked предоставляет возможность полностью контролировать процесс рендеринга с помощью кастомного объекта Renderer. Пример базового подхода:

const { marked } = require('marked');

const renderer = new marked.Renderer();

// Разрешение только определенных заголовков
renderer.heading = (text, level) => {
  if ([1, 2, 3].includes(level)) {
    return `<h${level}>${text}</h${level}>`;
  }
  return '';
};

// Разрешение только безопасных ссылок
renderer.link = (href, title, text) => {
  if (href.startsWith('http://') || href.startsWith('https://')) {
    return `<a href="${href}" title="${title || ''}">${text}</a>`;
  }
  return text;
};

marked.setOptions({ renderer });

В этом примере используются жёсткие фильтры, позволяющие рендерить только определённые заголовки и безопасные ссылки. Все остальное игнорируется, что снижает риск внедрения небезопасного HTML.


Контроль разрешённых тегов и элементов

Whitelist-подход требует явного перечисления разрешённых Markdown-элементов. В Marked это достигается через комбинацию:

  1. Custom Renderer — позволяет задавать обработку только нужных тегов.
  2. Token Filters — обработка токенов перед рендерингом.

Пример фильтрации списков и параграфов:

const lexer = new marked.Lexer();
const tokens = lexer.lex(markdownString);

const allowedTypes = ['paragraph', 'list', 'list_item'];
const filteredTokens = tokens.filter(token => allowedTypes.includes(token.type));

const html = marked.parser(filteredTokens, { renderer });

Здесь сначала Markdown разбивается на токены с помощью Lexer, затем остаются только разрешённые типы, после чего происходит рендеринг.


Управление атрибутами HTML

Whitelist-подход также включает ограничение атрибутов тегов. Например, можно разрешить только href для ссылок и запретить любые другие атрибуты:

renderer.link = (href, title, text) => {
  const allowedProtocols = ['http:', 'https:'];
  try {
    const url = new URL(href);
    if (!allowedProtocols.includes(url.protocol)) return text;
  } catch {
    return text;
  }
  return `<a href="${href}">${text}</a>`;
};

Это предотвращает использование jav * ascript: или других потенциально опасных схем URL.


Интеграция с sanitize-функциями

Marked поддерживает опцию sanitize, но она ограничена и не покрывает всех кейсов. Более надёжно использовать whitelist вместе с внешними библиотеками очистки HTML, например DOMPurify:

const DOMPurify = require('dompurify');
const html = marked(markdownString, { renderer });
const safeHtml = DOMPurify.sanitize(html, { ALLOWED_TAGS: ['p','a','ul','li','h1','h2','h3'] });

Такой подход объединяет возможности Marked с проверкой whitelist на уровне HTML, что обеспечивает максимальную безопасность.


Продвинутые техники фильтрации

  • Фильтрация изображений: разрешение только определённых доменов или форматов файлов.
  • Ограничение стилей: запрет <style> и inline-стилей через фильтры токенов.
  • Разделение контента и рендеринга: токены Markdown обрабатываются отдельно, затем передаются строго разрешённые токены в рендерер.

Пример фильтрации изображений:

renderer.image = (href, title, text) => {
  if (href.startsWith('https://trusted.com/')) {
    return `<img src="${href}" alt="${text}" title="${title || ''}"/>`;
  }
  return '';
};

Практические рекомендации

  • Явно указывать разрешённые типы элементов и атрибуты.
  • Использовать комбинацию Renderer + токен-фильтры + внешняя очистка HTML.
  • Проверять все ссылки и изображения на допустимые протоколы и домены.
  • Регулярно обновлять whitelist при добавлении новых функций Markdown.

Whitelist-подход превращает Marked в безопасный инструмент для рендеринга Markdown, позволяя точно контролировать, что попадает в HTML, и минимизировать потенциальные риски безопасности.