Ограничение сложности

Markdown-it — это мощная библиотека для парсинга Markdown в JavaScript, обеспечивающая гибкость и расширяемость. Одним из ключевых аспектов работы с Markdown-it является контроль сложности обрабатываемого Markdown, что важно для предотвращения чрезмерной нагрузки на процессор при работе с большими или потенциально вредоносными документами.

Ограничение глубины вложенности

Markdown позволяет создавать вложенные структуры, такие как списки внутри списков или блоки цитат. При отсутствии контроля глубины вложенности существует риск бесконечной рекурсии или значительной нагрузки на парсер.

В Markdown-it для ограничения глубины используется опция maxNesting:

const MarkdownIt = require('markdown-it');
const md = new MarkdownIt({
  maxNesting: 10  // Максимальная глубина вложенности
});
  • maxNesting задаёт максимально допустимую глубину для токенов.
  • Если глубина превышена, Markdown-it прекращает генерацию вложенных токенов и выдаёт безопасное, но упрощённое дерево.

Это особенно важно при обработке Markdown из ненадёжных источников, чтобы избежать DoS-атак через чрезмерно сложные структуры.

Ограничение длины отдельных токенов

Каждый элемент Markdown, будь то заголовок, абзац или ссылка, превращается в токен с содержимым. Длинные строки могут привести к росту потребления памяти и замедлению обработки.

Markdown-it позволяет реализовать контроль длины токенов через кастомные правила:

md.core.ruler.push('limit_token_length', state => {
  state.tokens.forEach(token => {
    if (token.content.length > 1000) {
      token.content = token.content.slice(0, 1000) + '...';
    }
  });
});
  • Этот подход позволяет автоматически обрезать слишком длинные элементы.
  • Он может применяться к заголовкам, блокам кода, параграфам и ссылкам.

Ограничение числа элементов в списках и таблицах

Большие списки или таблицы создают значительное количество токенов. Markdown-it не имеет встроенной опции для ограничения количества элементов, но это реализуется через пользовательские правила:

md.core.ruler.push('limit_list_items', state => {
  state.tokens.forEach(token => {
    if (token.type === 'bullet_list_open' || token.type === 'ordered_list_open') {
      let count = 0;
      for (let t of state.tokens) {
        if (t.type === 'list_item_open') count++;
        if (count > 50) {
          t.hidden = true; // скрываем лишние элементы
        }
      }
    }
  });
});
  • Такой метод предотвращает рендеринг чрезмерно длинных списков.
  • Можно гибко настраивать порог, в зависимости от требований производительности.

Ограничение времени обработки

Markdown-it выполняет синхронную обработку Markdown, что делает невозможным точный контроль времени выполнения через опции. Однако для защиты от «тяжёлых» документов можно использовать разделение на чанки:

  1. Разбить исходный текст на блоки.
  2. Обрабатывать блоки поочерёдно, с возможностью прерывания при превышении лимита.
  3. Объединять результаты после безопасной проверки.

Пример:

function safeRender(md, text, maxLength = 10000) {
  if (text.length > maxLength) {
    text = text.slice(0, maxLength);
  }
  return md.render(text);
}
  • Этот подход ограничивает количество обрабатываемого текста.
  • В комбинации с контролем токенов и глубины вложенности создаётся надёжная система защиты.

Ограничение сложных конструкций через плагины

Markdown-it поддерживает плагины, которые могут добавлять или ограничивать возможности парсера. Для ограничения сложности можно создавать плагины, проверяющие:

  • Максимальную глубину заголовков (h1h6).
  • Количество вложенных блоков цитат.
  • Максимальное число изображений и ссылок на странице.

Пример базового плагина для контроля изображений:

function limitImages(md, maxImages = 10) {
  md.core.ruler.push('limit_images', state => {
    let imageCount = 0;
    state.tokens.forEach(token => {
      if (token.type === 'inline') {
        token.children.forEach(child => {
          if (child.type === 'image') {
            imageCount++;
            if (imageCount > maxImages) child.hidden = true;
          }
        });
      }
    });
  });
}

md.use(limitImages, 5);
  • Этот подход обеспечивает строгий контроль мультимедийного контента.
  • Можно комбинировать с ограничением ссылок и других токенов для комплексной защиты.

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

  • Всегда устанавливать maxNesting, особенно для документов от пользователей.
  • Контролировать длину токенов и количество элементов в списках или таблицах.
  • Использовать кастомные правила или плагины для ограничения изображений, ссылок и вложенных блоков.
  • Ограничивать общий размер Markdown до разумного значения для предотвращения перегрузки памяти.
  • Комбинировать несколько уровней защиты для надёжной работы с Markdown на публичных сервисах.

Ограничение сложности в Markdown-it позволяет создавать безопасные и предсказуемые рендеры Markdown, избегая чрезмерной нагрузки на сервер и обеспечивая устойчивость системы к потенциально вредоносным документам.