
DOMPurify v3.4.13
DOMPurify — это DOM-only, сверхбыстрый, сверхтолерантный XSS-санитайзер для HTML, MathML и SVG. DOMPurify работает с безопасным набором настроек по умолчанию, но предлагает множество возможностей конфигурации и хуков. Демо:
DOMPurify
DOMPurify — это сверхбыстрый, сверхтолерантный XSS-санитайзер для HTML, MathML и SVG, работающий исключительно с DOM.
Он также очень прост в использовании и освоении. DOMPurify был запущен в феврале 2014 года и к настоящему моменту достиг версии v3.4.14.
DOMPurify работает на JavaScript и функционирует во всех современных браузерах (Safari (10+), Opera (15+), Edge, Firefox и Chrome — а также практически во всём остальном, что использует Blink, Gecko или WebKit). Он не ломается в MSIE или других устаревших браузерах. Он просто ничего не делает.
Обратите внимание, что DOMPurify v2.5.9 — это последняя версия с поддержкой MSIE. Для важных обновлений безопасности, совместимых с MSIE, используйте ветку 2.x.
Наши автоматические тесты охватывают 9 комбинаций браузеров/ОС на текущих движках (Chromium, Firefox и WebKit на Ubuntu, macOS и Windows) при каждом пуше, а отдельная матрица повторно запускает набор тестов на более старых снимках движков (примерно до Chromium 110, Firefox 108 и WebKit 16.4, возрастом около трёх лет), чтобы регрессии в устаревших браузерах тоже были обнаружены. Мы также запускаем Node.js v20, v22, v24, v25 и v26 с DOMPurify на jsdom. Известно, что более старые версии Node также работают, но... без гарантий.
DOMPurify написан специалистами по безопасности с обширным опытом в веб-атаках и XSS. Не бойтесь. Для более подробной информации, пожалуйста, также прочитайте о наших Целях безопасности и модели угроз. Пожалуйста, прочитайте это. Серьёзно. А если вам нравятся кровавые подробности, страница Классы атак и история обходов каталогизирует трюки с мутацией парсера, пространствами имён, клобберингом и шаблонами, от которых защищается DOMPurify.
Проект DOMPurify вдохновил создание API санитайзера HTML, который уже поставляется во многих браузерах. Та же возможность теперь стандартизируется непосредственно в спецификации WHATWG HTML.
Содержание
- Что он делает?
- Как его использовать?
- Есть ли демо?
- Что делать, если я нашёл уязвимость безопасности?
- Несколько примеров очистки, пожалуйста?
- Что поддерживается?
- А как насчёт устаревших браузеров, таких как Internet Explorer?
- А как насчёт DOMPurify и Trusted Types?
- Могу ли я настроить DOMPurify?
- Постоянная конфигурация
- Хуки
- Удалённая конфигурация
- Непрерывная интеграция
- Список рассылки по безопасности
- Кто внёс вклад?
Что он делает?
DOMPurify очищает HTML и предотвращает XSS-атаки. Вы можете передать DOMPurify, например, строку, полную грязного HTML, и он вернёт строку (если не настроено иначе) с чистым HTML. DOMPurify вырежет всё, что содержит опасный HTML, и тем самым предотвратит XSS-атаки и прочие неприятности. Он также чертовски быстр. Мы используем технологии, которые предоставляет браузер, и превращаем их в XSS-фильтр. Чем быстрее ваш браузер, тем быстрее будет DOMPurify.
Как его использовать?
Это просто. Просто подключите DOMPurify на свой сайт.
Использование неминифицированной версии (доступна source-map)```html
### Использование минифицированной и протестированной production-версии (доступна source-map)```html
<script type="text/javascript" src="dist/purify.min.js"></script>
После этого вы можете очистить строки, выполнив следующий код:```js const clean = DOMPurify.sanitize(dirty);
Или, возможно, это, если вам нравится работать с Angular или чем-то подобным:```js
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize('<b>hello there</b>');
Результирующий HTML можно записать в DOM-элемент с помощью innerHTML или в DOM с помощью document.write(). Это полностью на ваше усмотрение.
Обратите внимание, что по умолчанию мы разрешаем HTML, SVG и MathML. Если вам нужен только HTML, что может быть очень распространённым случаем использования, вы можете легко настроить и это:```js
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
### Есть ли какие-то подводные камни?
Учтите: если вы _сначала_ очистите HTML, а затем _после этого_ измените его, вы легко можете **свести на нет эффект очистки**. Если вы передаёте очищенную разметку другой библиотеке _после_ очистки, убедитесь, что эта библиотека не возится с HTML самостоятельно. См. [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model) для безопасных рецептов использования и тегов/атрибутов, о которых стоит подумать дважды, а также [Attack Classes & Bypass History](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History) — почему постобработка и изменение контекста разметки сводят на нет очистку.
### Ладно, понятно, двигаемся дальше
После очистки разметки вы также можете взглянуть на свойство `DOMPurify.removed` и узнать, какие элементы и атрибуты были отброшены. Пожалуйста, **не используйте** это свойство для принятия каких-либо критически важных решений в области безопасности. Это просто небольшой помощник для любопытных.
### Запуск DOMPurify на сервере
DOMPurify технически также работает на стороне сервера с Node.js. Наша поддержка стремится следовать [циклу выпусков Node.js](https://nodejs.org/en/about/previous-releases).
Запуск DOMPurify на сервере требует наличия DOM, что, вероятно, не является сюрпризом. Обычно инструментом выбора является [jsdom](https://github.com/jsdom/jsdom), и мы **настоятельно рекомендуем** использовать последнюю версию _jsdom_.
Почему? Потому что старые версии _jsdom_ известны своими багами, которые приводят к XSS, _даже если_ DOMPurify делает всё на 100% правильно. Существуют **известные векторы атак**, например, в _jsdom v19.0.0_, которые исправлены в _jsdom v20.0.0_ — и мы действительно рекомендуем держать _jsdom_ в актуальном состоянии именно из-за этого.
Также имейте в виду, что существуют такие инструменты, как [happy-dom](https://github.com/capricorn86/happy-dom), но на данный момент **они не считаются безопасными**. Комбинировать DOMPurify с _happy-dom_ в настоящее время не рекомендуется, и это, скорее всего, приведёт к XSS. О том, почему выбранный вами серверный DOM является частью вашей доверенной вычислительной базы, см. [Attack Classes & Bypass History](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History).
В остальном вы можете спокойно использовать DOMPurify на сервере. Возможно. Это действительно зависит от _jsdom_ или любого другого DOM, который вы используете на серверной стороне. Если вас это устраивает, вот как заставить это работать:```bash
npm install dompurify
npm install jsdom
Для jsdom (используйте актуальную версию), подойдёт следующее:```js const createDOMPurify = require('dompurify'); const { JSDOM } = require('jsdom');
const window = new JSDOM('').window; const DOMPurify = createDOMPurify(window); const clean = DOMPurify.sanitize('hello there');
Или даже так, если вы предпочитаете работать с импортами:```js
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const clean = purify.sanitize('<b>hello there</b>');
Если у вас возникают проблемы с его работой в вашем конкретном окружении, обратите внимание на замечательный проект isomorphic-dompurify, который решает множество проблем, с которыми могут столкнуться пользователи.```bash npm install isomorphic-dompurify
🛡️ Защита
- Шифрование: Все данные шифруются с использованием AES-256-GCM.
- Аутентификация: Поддерживается многофакторная аутентификация (MFA).
- Контроль доступа: Реализовано управление доступом на основе ролей (RBAC).
- Аудит: Все действия пользователей регистрируются в журнале аудита.
- Безопасное хранение: Пароли и ключи хранятся в аппаратном модуле безопасности (HSM).
- Сетевая безопасность: Весь трафик защищён с помощью TLS 1.3.
- Обнаружение вторжений: Встроенная система обнаружения вторжений (IDS) отслеживает подозрительную активность.
- Резервное копирование: Автоматическое резервное копирование выполняется каждые 24 часа.
- Восстановление: Механизмы аварийного восстановления обеспечивают непрерывность бизнеса.
- Соответствие: Инструмент соответствует стандартам GDPR, HIPAA и SOC 2.
import DOMPurify from 'isomorphic-dompurify';
const clean = DOMPurify.sanitize('<s>hello</s>');
```
## Есть ли демо?
Конечно, демо есть! [Поиграйте с DOMPurify](https://cure53.de/purify)
## Что делать, если я нашёл уязвимость в безопасности?
Прежде всего, пожалуйста, немедленно свяжитесь с нами по [электронной почте](mailto:[email protected]), чтобы мы могли заняться исправлением. [PGP-ключ](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xC26C858090F70ADA)
Кроме того, вы, вероятно, можете претендовать на вознаграждение за найденные ошибки! Замечательные люди из [Fastmail](https://www.fastmail.com/) используют DOMPurify в своих сервисах и добавили нашу библиотеку в область своей программы bug bounty. Так что, если вы найдёте способ обойти или ослабить DOMPurify, пожалуйста, также загляните на их сайт и в [информацию о bug bounty](https://www.fastmail.com/about/bugbounty/).
## Несколько примеров очистки, пожалуйста?
Как выглядит очищенная разметка? Ну, [демо](https://cure53.de/purify) показывает это на большом наборе вредоносных элементов. Но давайте также покажем несколько небольших примеров!```js
DOMPurify.sanitize(''); // becomes <img src="https://raw.githubusercontent.com/cure53/dompurify/main/x">
DOMPurify.sanitize('<svg><g/onload=alert(2)//<p>'); // becomes <svg><g></g></svg>
DOMPurify.sanitize('<p>abcdef</p>'); // becomes <p>abc</p>
DOMPurify.sanitize('<math><mi//xlink:href="data:x,<script>alert(4)</script>">'); // becomes <math><mi></mi></math>
DOMPurify.sanitize('<TABLE><tr><td>HELLO</tr></TABL>'); // becomes <table><tbody><tr><td>HELLO</td></tr></tbody></table>
DOMPurify.sanitize('<UL><li><A HREF=//google.com>click</UL>'); // becomes <ul><li><a href="//google.com">click</a></li></ul>
```
Это лишь малая часть. Полную таксономию классов атак, из которых взяты эти примеры — мутационный XSS, путаница пространств имён, DOM-клобберинг, выходы из rawtext и другие — см. в разделе [Attack Classes & Bypass History](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History).
## Что поддерживается?
DOMPurify в настоящее время поддерживает HTML5, SVG и MathML. По умолчанию DOMPurify разрешает CSS и пользовательские атрибуты данных HTML. DOMPurify также поддерживает Shadow DOM — и рекурсивно очищает DOM-шаблоны. DOMPurify также позволяет очищать HTML для использования с API jQuery `$()` и `elm.html()` без каких-либо известных проблем. Точный набор элементов и атрибутов, разрешённых по умолчанию, см. на странице вики [Default TAGs & ATTRIBUTEs allow-list & blocklist](https://github.com/cure53/DOMPurify/wiki/Default-TAGs-ATTRIBUTEs-allow-list-&-blocklist).
## А как насчёт устаревших браузеров, например Internet Explorer?
DOMPurify не делает ничего. Он просто возвращает ровно ту строку, которую вы ему передали. DOMPurify предоставляет свойство `isSupported`, которое сообщает, сможет ли он выполнить свою работу, чтобы вы могли придумать собственный запасной план.
## А как насчёт DOMPurify и Trusted Types?
В версии 1.0.9 в DOMPurify была добавлена поддержка [Trusted Types API](https://github.com/w3c/webappsec-trusted-types) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API)).
В версии 2.0.0 был добавлен флаг конфигурации для управления поведением DOMPurify в этом отношении.
Когда `DOMPurify.sanitize` используется в среде, где доступен Trusted Types API и для `RETURN_TRUSTED_TYPE` установлено значение `true`, он пытается вернуть значение `TrustedHTML` вместо строки (поведение для параметров конфигурации `RETURN_DOM` и `RETURN_DOM_FRAGMENT` не меняется).
Обратите внимание, что для создания политики в `trustedTypes` с помощью DOMPurify требуется `RETURN_TRUSTED_TYPE: false`, поскольку `createHTML` ожидает обычную строку, а не `TrustedHTML`. Пример ниже показывает это.```js
window.trustedTypes.createPolicy('default', {
createHTML: (to_escape) =>
DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }),
});
```
Когда параметр `TRUSTED_TYPES_POLICY` не указан, DOMPurify пытается создать собственную внутреннюю политику Trusted Types с именем `dompurify`. Если на вашей странице уже определена собственная политика вместе со строгим CSP (например, `trusted-types my-organization`), которая не допускает политику с именем `dompurify`, эта попытка блокируется браузером, и в журнал выводится предупреждение `TrustedTypes policy dompurify could not be created.` вместе с нарушением CSP.
Чтобы запретить DOMPurify создавать свою внутреннюю резервную политику, передайте `TRUSTED_TYPES_POLICY: null`. Это правильный выбор, когда вы вызываете `DOMPurify.sanitize` изнутри `createHTML` вашей собственной политики, и это означает, что вам не нужно добавлять `dompurify` в список разрешённых `trusted-types` вашего CSP.```js
window.trustedTypes.createPolicy('my-organization', {
createHTML: (input) =>
DOMPurify.sanitize(input, { TRUSTED_TYPES_POLICY: null }),
});
```
Не **передавайте** собственную политику обёртки обратно в DOMPurify как его `TRUSTED_TYPES_POLICY` (например, через `DOMPurify.setConfig({ TRUSTED_TYPES_POLICY: myPolicy })`), если `createHTML` этой политики уже вызывает `DOMPurify.sanitize`. Это циклично по определению — санитизация вызывала бы политику, а политика санитизировала бы, снова вызывая DOMPurify, — и DOMPurify выбросит понятное `TypeError`, чтобы предотвратить бесконечную рекурсию. Ваша собственная политика должна вызывать DOMPurify; DOMPurify не должен быть настроен на вызов вашей политики.
Если вы хотите, чтобы этот шаблон политики `default` применялся автоматически ко всей странице — так, чтобы каждый HTML-приёмник был санитизирован, включая устаревший код, сторонние виджеты и тысячи присваиваний `innerHTML`, которые сложно найти или переписать, — взгляните на [DOMFortify](https://github.com/cure53/DOMFortify). Он устанавливает именно такую политику Trusted Types `default`, основанную на DOMPurify, и полностью отклоняет скриптовые приёмники (`eval`, `script.src`, ...). Это намеренно отдельный проект: DOMPurify остаётся узконаправленным санитайзером, а DOMFortify берёт на себя уровень принудительного применения на уровне документа, который намеренно выходит за рамки DOMPurify.
## Могу ли я настроить DOMPurify?
Да. Включённые значения конфигурации по умолчанию уже довольно хороши — но вы, конечно, можете их переопределить. Загляните в папку [`/demos`](https://github.com/cure53/DOMPurify/tree/main/demos), чтобы увидеть множество примеров того, как можно [настроить DOMPurify](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this).
Прежде чем расширять список разрешённых элементов (`ADD_TAGS`, `ADD_ATTR`, `CUSTOM_ELEMENT_HANDLING`, …) или ослаблять настройки по умолчанию, стоит бегло просмотреть [теги и атрибуты, о которых стоит подумать дважды](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model#dangerous-tags-and-attributes-think-twice-before-allow-listing) — некоторые из них опасны неочевидными способами.
### Общие настройки```js
// strip {{ ... }}, ${ ... } and <% ... %> to make output safe for template systems
// be careful please, this mode is not recommended for production usage.
// allowing template parsing in user-controlled HTML is not advised at all.
// only use this mode if there is really no alternative.
const clean = DOMPurify.sanitize(dirty, { SAFE_FOR_TEMPLATES: true });
// change how e.g. comments containing risky HTML characters are treated.
// be very careful, this setting should only be set to `false` if you really only handle
// HTML and nothing else, no SVG, MathML or the like.
// Otherwise, changing from `true` to `false` will lead to XSS in this or some other way.
const clean = DOMPurify.sanitize(dirty, { SAFE_FOR_XML: false });
```
### Управление нашими списками разрешений и блокировок```js
// allow only <b> elements, very strict
const clean = DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b'] });
// allow only <b> and <q> with style attributes
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'q'],
ALLOWED_ATTR: ['style'],
});
// allow all safe HTML elements but neither SVG nor MathML
// note that the USE_PROFILES setting will override the ALLOWED_TAGS setting
// so don't use them together
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
// allow all safe SVG elements and SVG Filters, no HTML or MathML
const clean = DOMPurify.sanitize(dirty, {
USE_PROFILES: { svg: true, svgFilters: true },
});
// allow all safe MathML elements and SVG, but no SVG Filters
const clean = DOMPurify.sanitize(dirty, {
USE_PROFILES: { mathMl: true, svg: true },
});
// change the default namespace from HTML to something different
const clean = DOMPurify.sanitize(dirty, {
NAMESPACE: 'http://www.w3.org/2000/svg',
});
// leave all safe HTML as it is and add <style> elements to block-list
const clean = DOMPurify.sanitize(dirty, { FORBID_TAGS: ['style'] });
// leave all safe HTML as it is and add style attributes to block-list
const clean = DOMPurify.sanitize(dirty, { FORBID_ATTR: ['style'] });
// extend the existing array of allowed tags and add <my-tag> to allow-list
const clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['my-tag'] });
// extend the existing array of allowed attributes and add my-attr to allow-list
const clean = DOMPurify.sanitize(dirty, { ADD_ATTR: ['my-attr'] });
// use functions to control which additional tags and attributes are allowed
const allowlist = {
one: ['attribute-one'],
two: ['attribute-two'],
};
const clean = DOMPurify.sanitize(
'<one attribute-one="1" attribute-two="2"></one><two attribute-one="1" attribute-two="2"></two>',
{
ADD_TAGS: (tagName) => {
return Object.keys(allowlist).includes(tagName);
},
ADD_ATTR: (attributeName, tagName) => {
return allowlist[tagName]?.includes(attributeName) || false;
},
}
); // <one attribute-one="1"></one><two attribute-two="2"></two>
// prohibit ARIA attributes, leave other safe HTML as is (default is true)
const clean = DOMPurify.sanitize(dirty, { ALLOW_ARIA_ATTR: false });
// prohibit HTML5 data attributes, leave other safe HTML as is (default is true)
const clean = DOMPurify.sanitize(dirty, { ALLOW_DATA_ATTR: false });
```
### Поведение управления, связанное с пользовательскими элементами```js
// DOMPurify allows to define rules for Custom Elements. When using the CUSTOM_ELEMENT_HANDLING
// literal, it is possible to define exactly what elements you wish to allow (by default, none are allowed).
//
// The same goes for their attributes. By default, the built-in or configured allow.list is used.
//
// You can use a RegExp literal to specify what is allowed or a predicate, examples for both can be seen below.
// When using a predicate function for attributeNameCheck, it can optionally receive the tagName as a second parameter
// for more granular control over which attributes are allowed for specific elements.
// The default values are very restrictive to prevent accidental XSS bypasses. Handle with great care!
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: null, // no custom elements are allowed
attributeNameCheck: null, // default / standard attribute allow-list is used
allowCustomizedBuiltInElements: false, // no customized built-ins allowed
},
}
); // <div is=""></div>
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: /^foo-/, // allow all tags starting with "foo-"
attributeNameCheck: /baz/, // allow all attributes containing "baz"
allowCustomizedBuiltInElements: true, // customized built-ins are allowed
},
}
); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: (tagName) => tagName.match(/^foo-/), // allow all tags starting with "foo-"
attributeNameCheck: (attr) => attr.match(/baz/), // allow all containing "baz"
allowCustomizedBuiltInElements: true, // allow customized built-ins
},
}
); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
// Example with attributeNameCheck receiving tagName as a second parameter
const clean = DOMPurify.sanitize(
'<element-one attribute-one="1" attribute-two="2"></element-one><element-two attribute-one="1" attribute-two="2"></element-two>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: (tagName) => tagName.match(/^element-(one|two)$/),
attributeNameCheck: (attr, tagName) => {
if (tagName === 'element-one') {
return ['attribute-one'].includes(attr);
} else if (tagName === 'element-two') {
return ['attribute-two'].includes(attr);
} else {
return false;
}
},
allowCustomizedBuiltInElements: false,
},
}
); // <element-one attribute-one="1"></element-one><element-two attribute-two="2"></element-two>
```
### Поведение управления, связанное со значениями URI```js
// extend the existing array of elements that can use Data URIs
const clean = DOMPurify.sanitize(dirty, { ADD_DATA_URI_TAGS: ['a', 'area'] });
// extend the existing array of elements that are safe for URI-like values (be careful, XSS risk)
const clean = DOMPurify.sanitize(dirty, { ADD_URI_SAFE_ATTR: ['my-attr'] });
```
### Управление допустимыми значениями атрибутов```js
// allow external protocol handlers in URL attributes (default is false, be careful, XSS risk)
// by default only http, https, ftp, ftps, tel, mailto, callto, sms, cid, xmpp and matrix are allowed.
const clean = DOMPurify.sanitize(dirty, { ALLOW_UNKNOWN_PROTOCOLS: true });
// allow specific protocol handlers in URL attributes via regex (default is false, be careful, XSS risk)
// by default only (protocol-)relative URLs, http, https, ftp, ftps, tel, mailto, callto, sms, cid, xmpp and matrix are allowed.
// Default RegExp: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
});
```
### Влияние на тип возвращаемого значения```js
// return a DOM HTMLBodyElement instead of an HTML string (default is false)
const clean = DOMPurify.sanitize(dirty, { RETURN_DOM: true });
// return a DOM DocumentFragment instead of an HTML string (default is false)
const clean = DOMPurify.sanitize(dirty, { RETURN_DOM_FRAGMENT: true });
// use the RETURN_TRUSTED_TYPE flag to turn on Trusted Types support if available
const clean = DOMPurify.sanitize(dirty, { RETURN_TRUSTED_TYPE: true }); // will return a TrustedHTML object instead of a string if possible
// use a provided Trusted Types policy
const clean = DOMPurify.sanitize(dirty, {
// supplied policy must define createHTML and createScriptURL
TRUSTED_TYPES_POLICY: trustedTypes.createPolicy('dompurify', {
createHTML(s) {
return s;
},
createScriptURL(s) {
return s;
},
}),
});
// opt out of DOMPurify's internal `dompurify` Trusted Types policy entirely
// (useful when your CSP `trusted-types` allowlist does not include `dompurify`)
const clean = DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: null });
```
### Влияние на то, как мы выполняем санитизацию```js
// return entire document including <html> tags (default is false)
const clean = DOMPurify.sanitize(dirty, { WHOLE_DOCUMENT: true });
// disable DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here)
const clean = DOMPurify.sanitize(dirty, { SANITIZE_DOM: false });
// enforce strict DOM Clobbering protection via namespace isolation (default is false)
// when enabled, isolates the namespace of named properties (i.e., `id` and `name` attributes)
// from JS variables by prefixing them with the string `user-content-`
const clean = DOMPurify.sanitize(dirty, { SANITIZE_NAMED_PROPS: true });
// keep an element's content when the element is removed (default is true)
const clean = DOMPurify.sanitize(dirty, { KEEP_CONTENT: false });
// glue elements like style, script or others to document.body and prevent unintuitive browser behavior in several edge-cases (default is false)
const clean = DOMPurify.sanitize(dirty, { FORCE_BODY: true });
// remove all <a> elements under <p> elements that are removed
const clean = DOMPurify.sanitize(dirty, {
FORBID_CONTENTS: ['a'],
FORBID_TAGS: ['p'],
});
// extend the default FORBID_CONTENTS list to also remove <a> elements under <p> elements
const clean = DOMPurify.sanitize(dirty, {
ADD_FORBID_CONTENTS: ['a'],
FORBID_TAGS: ['p'],
});
// change the parser type so sanitized data is treated as XML and not as HTML, which is the default
const clean = DOMPurify.sanitize(dirty, {
PARSER_MEDIA_TYPE: 'application/xhtml+xml',
});
```
### Влияние на то, где мы выполняем санитизацию```js
// use the IN_PLACE mode to sanitize a node "in place", which is much faster depending on how you use DOMPurify
const dirty = document.createElement('a');
dirty.setAttribute('href', 'javascript:alert(1)');
const clean = DOMPurify.sanitize(dirty, { IN_PLACE: true }); // see https://github.com/cure53/DOMPurify/issues/288 for more info
```
Здесь есть [ещё больше примеров](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this), показывающих, как можно запускать, настраивать и конфигурировать DOMPurify под свои нужды.
## Постоянная конфигурация
Вместо того чтобы каждый раз передавать одну и ту же конфигурацию в `DOMPurify.sanitize`, вы можете использовать метод `DOMPurify.setConfig`. Ваша конфигурация будет сохраняться до следующего вызова `DOMPurify.setConfig` или до тех пор, пока вы не вызовете `DOMPurify.clearConfig` для её сброса. Помните, что активна только одна конфигурация, а это значит, что как только она установлена, все дополнительные параметры конфигурации, переданные в `DOMPurify.sanitize`, игнорируются.
## Хуки
DOMPurify позволяет расширить его функциональность, прикрепив одну или несколько функций с помощью метода `DOMPurify.addHook` к одному из следующих хуков:
- `beforeSanitizeElements`
- `uponSanitizeElement` (без 's' — вызывается для каждого элемента)
- `afterSanitizeElements`
- `beforeSanitizeAttributes`
- `uponSanitizeAttribute`
- `afterSanitizeAttributes`
- `beforeSanitizeShadowDOM`
- `uponSanitizeShadowNode`
- `afterSanitizeShadowDOM`
Он передаёт в обратный вызов текущий обрабатываемый DOM-узел, при необходимости литерал с проверенными данными узла и атрибутов, а также конфигурацию DOMPurify. Посмотрите [демо MentalJS hook](https://github.com/cure53/DOMPurify/blob/main/demos/hooks-mentaljs-demo.html), чтобы увидеть, как можно удобно использовать этот API.
_Пример_:```js
DOMPurify.addHook(
'uponSanitizeAttribute',
function (currentNode, hookEvent, config) {
// Do something with the current node
// You can also mutate hookEvent for current node (i.e. set hookEvent.forceKeepAttr = true)
// For other than 'uponSanitizeAttribute' hook types hookEvent equals to null
}
);
```
### Примечание о вызове `sanitize()` из хука
**`DOMPurify.sanitize()` не является реентерабельным.** Пожалуйста, не вызывайте его изнутри хука или из колбэка конфигурации, такого как `CUSTOM_ELEMENT_HANDLING.tagNameCheck` или `attributeNameCheck`. Эти колбэки выполняются в _середине_ активного прохода санитайзера.
Вложенный вызов `sanitize()` заново считывает переданную ему конфигурацию и, тем самым, **заменяет конфигурацию, которую всё ещё использует внешний проход**. Остальная часть внешнего документа затем санитизируется в соответствии с конфигурацией вложенного вызова, а не вашей. Поскольку вложенный вызов обычно выполняется с конфигурацией по умолчанию, строгий список разрешённых тегов `ALLOWED_TAGS` может незаметно расшириться обратно до стандартного в середине документа, без ошибок и предупреждений.
Если вам нужно санитизировать вложенную разметку, например, HTML-фрагмент, содержащийся внутри значения атрибута, у вас есть два безопасных варианта. Либо задайте свою конфигурацию один раз с помощью [`DOMPurify.setConfig`](#persistent-configuration) вместо передачи её при каждом вызове, поскольку постоянная конфигурация используется вложенным вызовом и остаётся в силе на протяжении всего прохода; либо соберите фрагменты во время хука и санитизируйте их отдельным вызовом `sanitize()` _после_ того, как внешний вызов вернёт результат.
## Удалённая конфигурация
| Опция | С версии | Примечание |
| --------------- | -------- | ------------------------- |
| SAFE_FOR_JQUERY | 2.1.0 | Замена не требуется. |
## Непрерывная интеграция
В настоящее время мы используем GitHub Actions в сочетании с Playwright. Это позволяет нам подтверждать при каждом коммите, что всё работает в соответствующих современных браузерах, а отдельный запланированный и запускаемый при слиянии рабочий процесс повторно запускает набор тестов на снимках старых движков, чтобы также выявлять поломки в устаревших браузерах. Посмотрите журналы сборки здесь: https://github.com/cure53/DOMPurify/actions
Вы также можете запускать локальные тесты, выполнив `npm run test`.
Все значимые коммиты будут подписаны ключом `0x24BB6BF4` для дополнительной безопасности (с 8 апреля 2016 года).
### Разработка и участие
#### Установка (`npm i`)
Мы официально поддерживаем `npm`. Рабочий процесс GitHub Actions настроен на установку зависимостей с помощью `npm`. При использовании устаревшей версии `npm` мы не можем полностью гарантировать версии установленных зависимостей, что может привести к непредвиденным проблемам.
#### Скрипты
Мы используем ESLint через `xo` как часть нашего предкоммитного рабочего процесса для обеспечения согласованности кода. Кроме того, мы используем [Prettier](https://github.com/prettier/prettier) для форматирования исходного кода и Markdown, а ресурсы `/dist` собираются с помощью `rollup`.
Вот наши npm-скрипты:
- `npm run dev` — сборка неминифицированного UMD-бандла с отслеживанием изменений исходников
- `npm run test` — проверка исходников линтером, запуск тестов через jsdom и запуск браузерных тестов в Chromium через Playwright
- `npm run test:jsdom` — запуск только тестов через jsdom
- `npm run test:happydom` — запуск набора тестов через happy-dom (неподдерживаемая среда; сохраняется как проверка устойчивости, а не как обещание совместимости)
- `npm run test:browser` — запуск только тестов через Playwright
- `npm run test:browser:legacy` — запуск набора тестов на старых браузерных движках (укажите `PW_MODULE` на закреплённую старую установку Playwright; см. `.github/workflows/legacy-browsers.yml`)
- `npm run test:ci` — запуск CI-тестов для jsdom и Playwright
- `npm run test:fuzz` — запуск небольшого фаззера, покрывающего `sanitize()` и CONFIG
- `npm run bench` — запуск jsdom-микробенчмарка по собранному `dist/purify.cjs` (сначала соберите; `--json` и `--compare a.json b.json` поддерживают A/B-запуски между ветками — результаты ориентировочны, подтверждайте пользовательские заявления в реальных браузерах)
- `npm run coverage` — сборка инструментированного бандла, запуск набора тестов jsdom и запись локального HTML-отчёта о покрытии строк/веток в `coverage/index.html` (только в рамках jsdom, не запускается в CI)
- `npm run build:cov` — только сборка инструментированного бандла покрытия
- `npm run lint` — проверка исходников с помощью ESLint через xo
- `npm run format` — форматирование исходников JavaScript/TypeScript и Markdown с помощью Prettier
- `npm run format:js` — форматирование только исходников JavaScript/TypeScript
- `npm run format:md` — форматирование только файлов Markdown
- `npm run build` — сборка объявлений типов и дистрибутивных бандлов, затем исправление и очистка сгенерированных типов
- `npm run build:types` — только генерация файлов объявлений TypeScript
- `npm run build:rollup` — сборка всех бандлов Rollup
- `npm run build:umd` — только сборка неминифицированного UMD-бандла
- `npm run build:umd:min` — только сборка минифицированного UMD-бандла
- `npm run build:es` — только сборка ES-модульного бандла
- `npm run build:cjs` — только сборка CommonJS-бандла
- `npm run build:fix-types` — постобработка сгенерированных файлов типов
- `npm run build:cleanup` — очистка временных сгенерированных выходных данных типов
- `npm run verify-typescript` — запуск скрипта проверки TypeScript
- `npm run commit-amend-build` — запуск вспомогательного скрипта мейнтейнера для изменения выходных данных сборки
Примечание: все скрипты запускаются через `npm run <script>`.
Существуют и другие npm-скрипты, но они в основном предназначены для интеграции с CI или являются «приватными», например, для изменения дистрибутивных файлов сборки при каждом коммите.
## Список рассылки по безопасности
Мы поддерживаем список рассылки, который уведомляет о публикации **критически важного для безопасности** релиза DOMPurify. Это означает, что если кто-то нашёл обходной путь и мы исправили его релизом (что всегда происходит при обнаружении обхода), на этот список отправляется письмо. Обычно это происходит в течение нескольких минут или часов после узнавания об обходе. Подписаться на список можно здесь:
[https://lists.ruhr-uni-bochum.de/mailman/listinfo/dompurify-security](https://lists.ruhr-uni-bochum.de/mailman/listinfo/dompurify-security)
Релизы функций не анонсируются в этом списке.
## Кто внёс вклад?
Многие люди помогли DOMPurify стать тем, чем он является сегодня, и они заслуживают признания!
[offset](https://github.com/offset), [Bankde](https://github.com/Bankde), [lukewarlow](https://github.com/lukewarlow), [DEMON1A](https://github.com/DEMON1A), [fg0x0](https://github.com/fg0x0), [kodareef5](https://github.com/kodareef5), [DavidOliver](https://github.com/DavidOliver), [1Jesper1](https://github.com/1Jesper1), [bencalif](https://github.com/bencalif), [trace37labs](https://github.com/trace37labs), [eddieran](https://github.com/eddieran), [christos-eth](https://github.com/christos-eth), [researchatfluidattacks](https://github.com/researchatfluidattacks), [frevadiscor](https://github.com/frevadiscor), [Rotzbua](https://github.com/Rotzbua), [binhpv](https://github.com/binhpv), [MariusRumpf](https://github.com/MariusRumpf), [prasadrajandran](https://github.com/prasadrajandran), [Cybozu 💛💸](https://github.com/cybozu), [hata6502 💸](https://github.com/hata6502), [openclaw 💸](https://github.com/openclaw), [intra-mart-dh 💸](https://github.com/intra-mart-dh), [nelstrom ❤️](https://github.com/nelstrom), [hash_kitten ❤️](https://twitter.com/hash_kitten), [kevin_mizu ❤️](https://twitter.com/kevin_mizu), [icesfont ❤️](https://github.com/icesfont), [reduckted ❤️](https://github.com/reduckted), [dcramer 💸](https://github.com/dcramer), [JGraph 💸](https://github.com/jgraph), [baekilda 💸](https://github.com/baekilda), [Healthchecks 💸](https://github.com/healthchecks), [Sentry 💸](https://github.com/getsentry), [jarrodldavis 💸](https://github.com/jarrodldavis), [CynegeticIO](https://github.com/CynegeticIO), [ssi02014 ❤️](https://github.com/ssi02014), [GrantGryczan](https://github.com/GrantGryczan), [Lowdefy](https://twitter.com/lowdefy), [granlem](https://twitter.com/MaximeVeit), [oreoshake](https://github.com/oreoshake), [tdeekens ❤️](https://github.com/tdeekens), [peernohell ❤️](https://github.com/peernohell), [is2ei](https://github.com/is2ei), [SoheilKhodayari](https://github.com/SoheilKhodayari), [franktopel](https://github.com/franktopel), [NateScarlet](https://github.com/NateScarlet), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb ❤️](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro), [@CmdEngineer\_](https://twitter.com/CmdEngineer_), [@avr4mit](https://twitter.com/avr4mit), [davecardwell](https://github.com/davecardwell), [Develop-KIM](https://github.com/Develop-KIM), [asamuzaK](https://github.com/asamuzaK), [fishjojo1 ❤️](https://github.com/fishjojo1), [Rikuxx0](https://github.com/Rikuxx0), [donmccurdy](https://github.com/donmccurdy), [hhk-png](https://github.com/hhk-png), [elrion018](https://github.com/elrion018), [michalnieruchalski-tiugo](https://github.com/michalnieruchalski-tiugo), [reey](https://github.com/reey), [KanhaKanhaiya](https://github.com/KanhaKanhaiya), [odaysec](https://github.com/odaysec), [Akokonunes](https://github.com/Akokonunes), [alirezarouhbakhsh](https://github.com/alirezarouhbakhsh), [Jaybhade](https://github.com/Jaybhade) и особенно [@securitymb ❤️](https://twitter.com/securitymb) и [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)