Плагин $sanitize — безопасный рендер HTML

Плагин предоставляет функцию $sanitize(html, options?) для безопасной очистки и рендера HTML‑контента на клиенте. Внутри используется DOMPurify через пакет isomorphic-dompurify. Плагин подключается как Nuxt‑провайдер и доступен из компонентов, а также через вспомогательный компонент <SafeHtml />.

  • Файлы:
    • app/plugins/dompurify.ts — реализация и базовая конфигурация DOMPurify, хуки
    • app/types/dompurify.d.ts — типы для Nuxt $sanitize
    • app/components/base/SafeHtml.vue — обёртка для шаблонов (v-html с предварительной очисткой)

Быстрый старт

1) В шаблоне через компонент SafeHtml

<!-- Строка с потенциально небезопасным HTML -->
<SafeHtml :html="'<p>Пример <strong>жирного</strong> текста и <a href=\"javascript:alert(1)\">ссылки</a>.</p>'" />

2) В коде напрямую через $sanitize

const { $sanitize } = useNuxtApp()
const raw = '<p>Текст с <img src="x" onerror="alert(1)"> подозрительными атрибутами.</p>'
const safe = $sanitize(raw)
// затем можно отрисовать: <div v-html="safe" />

Конфигурация по умолчанию

Определена в app/plugins/dompurify.ts и объединяется с пользовательскими опциями, которые вы передаёте вторым параметром.

Ключевые моменты базовой конфигурации:

  • USE_PROFILES: { html: true }
  • Разрешённые URI‑схемы: только безопасные (https, http, mailto, tel, ftp, data:image/* base64)
  • ADD_ATTR: class, id, style, target, rel, title, data-*; медиа атрибуты (src, srcset, sizes, alt, width, height), ссылки (href, download, name), iframe/video (allow, allowfullscreen, frameborder, referrerpolicy, loading)
  • ADD_TAGS: iframe, video, source, picture, figure, figcaption, table и связанные элементы
  • ADD_URI_SAFE_ATTR: 'src', 'href', 'srcset'

Особые хуки DOMPurify:

  • uponSanitizeElement — фильтрует <iframe>:
    • запрещает srcdoc
    • оставляет только HTTPS
    • белый список источников: YouTube/Youtube‑nocookie/Vimeo в формате embed
  • afterSanitizeAttributes — обогащает <a> и <iframe>:
    • для ссылок убирает javascript: и data:text/html, проставляет target="_blank" и rel="noopener noreferrer nofollow"
    • для iframe добавляет безопасные атрибуты: loading="lazy", referrerpolicy="no-referrer", allow, а также рекомендует sandbox

Пользовательские опции

Функция $sanitize(html, options?) принимает опциональный объект DOMPurify Config и сливает его с базовой конфигурацией плагина.

Примеры:

const { $sanitize } = useNuxtApp()

// 1) Жёстче — запретить все iframe на этой вставке
const html1 = $sanitize(rawHtml, { FORBID_TAGS: ['iframe'] })

// 2) Разрешить дополнительные теги/атрибуты только для одного вызова
const html2 = $sanitize(rawHtml, {
  ADD_TAGS: ['mark'],
  ADD_ATTR: ['aria-label']
})

// 3) Разрешить data: картинки в base64 (по умолчанию уже разрешены в плагине)
const html3 = $sanitize(rawHtml, {
  ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|ftp|data:image\/(?:png|gif|jpeg|webp);base64),?)/i
})

Важно: переданные опции перекрывают базовые только в пределах одного вызова. Хуки безопасности (например, запрет srcdoc у <iframe>) всё равно отработают.

Компонент SafeHtml

Упрощает рендер HTML в шаблонах:

  • Пропсы:
    • html?: string — исходная строка HTML
    • options?: DOMPurify Config — кастомные опции для данного рендера
  • Использование:
<SafeHtml :html="content" :options="{ FORBID_TAGS: ['iframe'] }" />

Исходник (фрагмент):

// app/components/base/SafeHtml.vue
import type { Config } from 'isomorphic-dompurify'

interface Props {
  html?: string
  options?: Config
}

const props = defineProps<Props>()
const { $sanitize } = useNuxtApp()
const safeHtml = computed(() => $sanitize(props.html ?? '', props.options))

Безопасность и рекомендации

  • Никогда не вставляйте HTML из недоверенных источников напрямую через v-html — всегда используйте $sanitize/SafeHtml.
  • Не полагайтесь только на клиентский санитайз: сервер также должен фильтровать/экранировать пользовательский ввод.
  • Старайтесь не использовать style и инлайн‑скрипты — они либо вырежутся, либо могут ухудшить доступность.
  • Для <iframe> используйте только проверенные источники и форматы embed; при необходимости сузьте белый список через собственный хук или FORBID_TAGS.

Частые вопросы

  • Можно ли отключить target/_blank у ссылок? — Да, передайте свой afterSanitizeAttributes хук в опциях или удалите атрибуты пост‑обработкой результата (не рекомендуется).
  • Как добавить своего провайдера видео? — Измените массив allowedOrigins в хуке uponSanitizeElement (файл плагина) или в опциях пропишите собственный ALLOWED_URI_REGEXP и удалите iframe через FORBID_TAGS, если не совпадает.
  • Работает ли на сервере? — Плагин ориентирован на клиентский рендер. Для SSR и безопасности на уровне бэкенда используйте серверные библиотеки санитайза.