diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 3f47e2602..f6ce75eac 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -9,6 +9,7 @@ on:
branches:
- main
- 3.x
+ - 2.x
pull_request:
jobs:
@@ -17,7 +18,7 @@ jobs:
strategy:
matrix:
- node-version: [16.x, 17.x, 18.x, 19.x]
+ node-version: [16.x, 17.x, 18.x, 19.x, 20.x, 21.x]
steps:
- name: Checkout
@@ -38,7 +39,7 @@ jobs:
with:
run: npm run test:ci
env:
- TEST_BROWSERSTACK: ${{ startsWith(matrix.node-version, '19') }}
- TEST_PROBE_ONLY: ${{ github.ref != 'refs/heads/main' }}
+ TEST_BROWSERSTACK: ${{ startsWith(matrix.node-version, '21') }}
+ TEST_PROBE_ONLY: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/2.x' }}
BS_USERNAME: ${{ secrets.BS_USERNAME }}
BS_ACCESSKEY: ${{ secrets.BS_ACCESSKEY }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8931faebb..0d7d28d93 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -38,14 +38,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# âšī¸ Command-line programs to run using the OS shell.
# đ https://git.io/JvXDl
@@ -59,4 +59,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/LICENSE b/LICENSE
index a5423bd8d..aed61cbb2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
DOMPurify
-Copyright 2023 Dr.-Ing. Mario Heiderich, Cure53
+Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53
DOMPurify is free software; you can redistribute it and/or modify it under the
terms of either:
diff --git a/README.md b/README.md
index 7f5234d8f..e8094265a 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,13 @@
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
-It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.0.6**.
+It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.2.0**.
DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Edge, Firefox and Chrome - as well as almost anything else using Blink, Gecko or WebKit). It doesn't break on MSIE or other legacy browsers. It simply does nothing.
-**Note that [DOMPurify v2.4.7](https://github.com/cure53/DOMPurify/releases/tag/2.4.6) is the latest version supporting MSIE. For important security updates compatible with MSIE, please use the [2.x branch](https://github.com/cure53/DOMPurify/tree/2.x).**
+**Note that [DOMPurify v2.5.7](https://github.com/cure53/DOMPurify/releases/tag/2.5.7) is the latest version supporting MSIE. For important security updates compatible with MSIE, please use the [2.x branch](https://github.com/cure53/DOMPurify/tree/2.x).**
-Our automated tests cover [19 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v16.x, v17.x, v18.x and v19.x, running DOMPurify on [jsdom](https://github.com/jsdom/jsdom). Older Node versions are known to work as well, but hey... no guarantees.
+Our automated tests cover [24 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v16.x, v17.x, v18.x and v19.x, running DOMPurify on [jsdom](https://github.com/jsdom/jsdom). Older Node versions are known to work as well, but hey... no guarantees.
DOMPurify is written by security people who have vast background in web attacks and XSS. Fear not. For more details please also read about our [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model). Please, read it. Like, really.
@@ -45,7 +45,7 @@ const clean = DOMPurify.sanitize(dirty);
Or maybe this, if you love working with Angular or alike:
```js
-import * as DOMPurify from 'dompurify';
+import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize('hello there');
```
@@ -57,10 +57,6 @@ Note that by default, we permit HTML, SVG **and** MathML. If you only need HTML,
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
```
-### Where are the TypeScript type definitions?
-
-They can be found here: [@types/dompurify](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/dompurify)
-
### Is there any foot-gun potential?
Well, please note, if you _first_ sanitize HTML and then modify it _afterwards_, you might easily **void the effects of sanitization**. If you feed the sanitized markup to another library _after_ sanitization, please be certain that the library doesn't mess around with the HTML on its own.
@@ -77,6 +73,8 @@ Running DOMPurify on the server requires a DOM to be present, which is probably
Why? Because older versions of _jsdom_ are known to be buggy in ways that result in XSS _even if_ DOMPurify does everything 100% correctly. There are **known attack vectors** in, e.g. _jsdom v19.0.0_ that are fixed in _jsdom v20.0.0_ - and we really recommend to keep _jsdom_ up to date because of that.
+Please also be aware that tools like [happy-dom](https://github.com/capricorn86/happy-dom) exist but **are not considered safe** at this point. Combining DOMPurify with _happy-dom_ is currently not recommended and will likely lead to XSS.
+
Other than that, you are fine to use DOMPurify on the server. Probably. This really depends on _jsdom_ or whatever DOM you utilize server-side. If you can live with that, this is how you get it to work:
```bash
@@ -156,6 +154,15 @@ In version 2.0.0, a config flag was added to control DOMPurify's behavior regard
When `DOMPurify.sanitize` is used in an environment where the Trusted Types API is available and `RETURN_TRUSTED_TYPE` is set to `true`, it tries to return a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM` and `RETURN_DOM_FRAGMENT` config options does not change).
+Note that in order to create a policy in `trustedTypes` using DOMPurify, `RETURN_TRUSTED_TYPE: false` is required, as `createHTML` expects a normal string, not `TrustedHTML`. The example below shows this.
+
+```js
+window.trustedTypes!.createPolicy('default', {
+ createHTML: (to_escape) =>
+ DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }),
+});
+```
+
## Can I configure DOMPurify?
Yes. The included default configuration values are pretty good already - but you can of course override them. Check out the [`/demos`](https://github.com/cure53/DOMPurify/tree/main/demos) folder to see a bunch of examples on how you can [customize DOMPurify](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this).
@@ -167,6 +174,13 @@ Yes. The included default configuration values are pretty good already - but you
// 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});
```
### Control our allow-lists and block-lists
@@ -354,15 +368,21 @@ _Example_:
```js
DOMPurify.addHook(
- 'beforeSanitizeElements',
+ 'uponSanitizeAttribute',
function (currentNode, hookEvent, config) {
- // Do something with the current node and return it
- // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true)
- return currentNode;
+ // 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
}
);
```
+## Removed Configuration
+
+| Option | Since | Note |
+|-----------------|-------|--------------------------|
+| SAFE_FOR_JQUERY | 2.1.0 | No replacement required. |
+
## Continuous Integration
We are currently using Github Actions in combination with BrowserStack. This gives us the possibility to confirm for each and every commit that all is going according to plan in all supported browsers. Check out the build logs here: https://github.com/cure53/DOMPurify/actions
@@ -409,10 +429,10 @@ Feature releases will not be announced to this list.
Many people helped and help DOMPurify become what it is and need to be acknowledged here!
-[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), [kevin_mizu](https://twitter.com/kevin_mizu), [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) and especially [@securitymb â¤ī¸](https://twitter.com/securitymb) & [@masatokinugawa â¤ī¸](https://twitter.com/masatokinugawa)
+[hash_kitten â¤ī¸](https://twitter.com/hash_kitten), [kevin_mizu â¤ī¸](https://twitter.com/kevin_mizu), [icesfont â¤ī¸](https://github.com/icesfont) [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) and especially [@securitymb â¤ī¸](https://twitter.com/securitymb) & [@masatokinugawa â¤ī¸](https://twitter.com/masatokinugawa)
## Testing powered by
- elements allowed
// Note: We want to also keep 's text content, so we add #text too
-var config = { ALLOWED_TAGS: ['p', '#text'], KEEP_CONTENT: false };
+const config = { ALLOWED_TAGS: ['p', '#text'], KEEP_CONTENT: false };
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Advanced Config Demo [Link](advanced-config-demo.html)
@@ -38,7 +38,7 @@ This is the relevant code:
```javascript
// Specify a configuration directive
-var config = {
+const config = {
ALLOWED_TAGS: ['p', '#text'], // only and text nodes
KEEP_CONTENT: false, // remove content from non-allow-listed nodes too
ADD_ATTR: ['kitty-litter'], // permit kitty-litter attributes
@@ -47,7 +47,7 @@ var config = {
};
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hooks Demo [Link](hooks-demo.html)
@@ -66,7 +66,7 @@ DOMPurify.addHook('beforeSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Add hooks and remove hooks [Link](hooks-removal-demo.html)
@@ -85,13 +85,13 @@ DOMPurify.addHook('beforeSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+let clean = DOMPurify.sanitize(dirty);
// now let's remove the hook again
console.log(DOMPurify.removeHook('beforeSanitizeAttributes'));
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+let clean = DOMPurify.sanitize(dirty);
```
### Hook to open all links in a new window [Link](hooks-target-blank-demo.html)
@@ -117,7 +117,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to white-list safe URI Schemes [Link](hooks-scheme-allowlist.html)
@@ -130,15 +130,15 @@ This is the relevant code:
```javascript
// allowed URI schemes
-var allowlist = ['http', 'https', 'ftp'];
+const allowlist = ['http', 'https', 'ftp'];
// build fitting regex
-var regex = RegExp('^(' + allowlist.join('|') + '):', 'gim');
+const regex = RegExp('^(' + allowlist.join('|') + '):', 'gim');
// Add a hook to enforce URI scheme allow-list
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// build an anchor to map URLs to
- var anchor = document.createElement('a');
+ const anchor = document.createElement('a');
// check all href attributes for validity
if (node.hasAttribute('href')) {
@@ -164,7 +164,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to allow and sand-box all JavaScript [Link](hooks-mentaljs-demo.html)
@@ -177,7 +177,7 @@ This is the relevant code:
```javascript
// allow script elements
-var config = {
+const config = {
ADD_TAGS: ['script'],
ADD_ATTR: ['onclick', 'onmouseover', 'onload', 'onunload'],
};
@@ -185,7 +185,7 @@ var config = {
// Add a hook to sanitize all script content with MentalJS
DOMPurify.addHook('uponSanitizeElement', function (node, data) {
if (data.tagName === 'script') {
- var script = node.textContent;
+ let script = node.textContent;
if (
!script ||
'src' in node.attributes ||
@@ -195,7 +195,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
return node.parentNode.removeChild(node);
}
try {
- var mental = MentalJS().parse({
+ let mental = MentalJS().parse({
options: {
eval: false,
dom: true,
@@ -212,7 +212,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
// Add a hook to sanitize all white-listed events with MentalJS
DOMPurify.addHook('uponSanitizeAttribute', function (node, data) {
if (data.attrName.match(/^on\w+/)) {
- var script = data.attrValue;
+ let script = data.attrValue;
try {
return (data.attrValue = MentalJS().parse({
options: {
@@ -228,7 +228,7 @@ DOMPurify.addHook('uponSanitizeAttribute', function (node, data) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hook to proxy all links [Link](hooks-link-proxy-demo.html)
@@ -264,7 +264,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to proxy all HTTP leaks including CSS [Link](hooks-proxy-demo.html)
@@ -277,28 +277,28 @@ This is the relevant code:
```javascript
// Specify proxy URL
-var proxy = 'https://my.proxy/?url=';
+const proxy = 'https://my.proxy/?url=';
// What do we allow? Not much for now. But it's tight.
-var config = {
+const config = {
FORBID_TAGS: ['svg'],
WHOLE_DOCUMENT: true,
};
// Specify attributes to proxy
-var attributes = ['action', 'background', 'href', 'poster', 'src'];
+const attributes = ['action', 'background', 'href', 'poster', 'src', 'srcset']
// specify the regex to detect external content
-var regex = /(url\("?)(?!data:)/gim;
+const regex = /(url\("?)(?!data:)/gim;
/**
* Take CSS property-value pairs and proxy URLs in values,
* then add the styles to an array of property-value pairs
*/
function addStyles(output, styles) {
- for (var prop = styles.length - 1; prop >= 0; prop--) {
+ for (let prop = styles.length - 1; prop >= 0; prop--) {
if (styles[styles[prop]]) {
- var url = styles[styles[prop]].replace(regex, '$1' + proxy);
+ let url = styles[styles[prop]].replace(regex, '$1' + proxy);
styles[styles[prop]] = url;
}
if (styles[styles[prop]]) {
@@ -312,8 +312,8 @@ function addStyles(output, styles) {
* then create matching CSS text for later application to the DOM
*/
function addCSSRules(output, cssRules) {
- for (var index = cssRules.length - 1; index >= 0; index--) {
- var rule = cssRules[index];
+ for (let index = cssRules.length - 1; index >= 0; index--) {
+ let rule = cssRules[index];
// check for rules with selector
if (rule.type == 1 && rule.selectorText) {
output.push(rule.selectorText + '{');
@@ -336,8 +336,8 @@ function addCSSRules(output, cssRules) {
// check for @keyframes rules
} else if (rule.type === rule.KEYFRAMES_RULE) {
output.push('@keyframes ' + rule.name + '{');
- for (var i = rule.cssRules.length - 1; i >= 0; i--) {
- var frame = rule.cssRules[i];
+ for (let i = rule.cssRules.length - 1; i >= 0; i--) {
+ let frame = rule.cssRules[i];
if (frame.type === 8 && frame.keyText) {
output.push(frame.keyText + '{');
if (frame.style) {
@@ -365,7 +365,7 @@ function proxyAttribute(url) {
// Add a hook to enforce proxy for leaky CSS rules
DOMPurify.addHook('uponSanitizeElement', function (node, data) {
if (data.tagName === 'style') {
- var output = [];
+ let output = [];
addCSSRules(output, node.sheet.cssRules);
node.textContent = output.join('\n');
}
@@ -374,7 +374,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
// Add a hook to enforce proxy for all HTTP leaks incl. inline CSS
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// Check all src attributes and proxy them
- for (var i = 0; i <= attributes.length - 1; i++) {
+ for (let i = 0; i <= attributes.length - 1; i++) {
if (node.hasAttribute(attributes[i])) {
node.setAttribute(
attributes[i],
@@ -385,12 +385,12 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// Check all style attribute values and proxy them
if (node.hasAttribute('style')) {
- var styles = node.style;
- var output = [];
- for (var prop = styles.length - 1; prop >= 0; prop--) {
+ let styles = node.style;
+ let output = [];
+ for (let prop = styles.length - 1; prop >= 0; prop--) {
// we re-write each property-value pair to remove invalid CSS
if (node.style[styles[prop]] && regex.test(node.style[styles[prop]])) {
- var url = node.style[styles[prop]].replace(regex, '$1' + proxy);
+ let url = node.style[styles[prop]].replace(regex, '$1' + proxy);
node.style[styles[prop]] = url;
}
output.push(styles[prop] + ':' + node.style[styles[prop]] + ';');
@@ -405,7 +405,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hook to sanitize SVGs shown via an `
+
And last but not least, thanks to [BrowserStack Open-Source Program](https://www.browserstack.com/open-source) for supporting this project with their services for free and delivering excellent, dedicated and very professional support on top of that.
diff --git a/bower.json b/bower.json
index dd812ac65..bbfbadedf 100644
--- a/bower.json
+++ b/bower.json
@@ -1,10 +1,10 @@
{
- "name": "DOMPurify",
- "version": "3.0.6",
+ "name": "dompurify",
+ "version": "3.2.0",
"homepage": "https://github.com/cure53/DOMPurify",
"author": "Cure53 ` tag. [Link](hooks-svg-demo.html)
@@ -423,14 +423,14 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean SVG string and allow the "filter" tag
-var clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['filter'] });
+const clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['filter'] });
// Remove partial XML comment left in the HTML
-var badTag = clean.indexOf(']>');
-var pureSvg = clean.substring(badTag < 0 ? 0 : 5, clean.length);
+let badTag = clean.indexOf(']>');
+let pureSvg = clean.substring(badTag < 0 ? 0 : 5, clean.length);
// Show sanitized content in
element
-var img = new Image();
+let img = new Image();
img.src = 'data:image/svg+xml;base64,' + window.btoa(pureSvg);
document.getElementById('sanitized').appendChild(img);
```
diff --git a/demos/advanced-config-demo.html b/demos/advanced-config-demo.html
index 92b29d55b..9009f244e 100644
--- a/demos/advanced-config-demo.html
+++ b/demos/advanced-config-demo.html
@@ -9,28 +9,32 @@
diff --git a/demos/basic-demo.html b/demos/basic-demo.html
index b7577b14f..e5b02cadf 100644
--- a/demos/basic-demo.html
+++ b/demos/basic-demo.html
@@ -9,15 +9,16 @@
diff --git a/demos/config-demo.html b/demos/config-demo.html index f84f901d8..c56e0caee 100644 --- a/demos/config-demo.html +++ b/demos/config-demo.html @@ -9,20 +9,23 @@ diff --git a/demos/hooks-demo.html b/demos/hooks-demo.html index bd6c099c3..15ab3cc20 100644 --- a/demos/hooks-demo.html +++ b/demos/hooks-demo.html @@ -9,23 +9,24 @@ diff --git a/demos/hooks-link-proxy-demo.html b/demos/hooks-link-proxy-demo.html index 02d0ed188..7a7019e5b 100644 --- a/demos/hooks-link-proxy-demo.html +++ b/demos/hooks-link-proxy-demo.html @@ -9,44 +9,42 @@ diff --git a/demos/hooks-mentaljs-demo.html b/demos/hooks-mentaljs-demo.html index e951316f4..5f4ddd730 100644 --- a/demos/hooks-mentaljs-demo.html +++ b/demos/hooks-mentaljs-demo.html @@ -11,84 +11,73 @@
diff --git a/demos/hooks-node-removal-demo.html b/demos/hooks-node-removal-demo.html index 012312b90..c3cef45b9 100644 --- a/demos/hooks-node-removal-demo.html +++ b/demos/hooks-node-removal-demo.html @@ -9,23 +9,21 @@ diff --git a/demos/hooks-node-removal2-demo.html b/demos/hooks-node-removal2-demo.html index f222c6ce7..1350f910e 100644 --- a/demos/hooks-node-removal2-demo.html +++ b/demos/hooks-node-removal2-demo.html @@ -9,53 +9,49 @@ diff --git a/demos/hooks-proxy-demo.html b/demos/hooks-proxy-demo.html index 17076ce1b..d6f976863 100644 --- a/demos/hooks-proxy-demo.html +++ b/demos/hooks-proxy-demo.html @@ -9,147 +9,100 @@ diff --git a/demos/hooks-removal-demo.html b/demos/hooks-removal-demo.html index 1134c7303..da9675a2b 100644 --- a/demos/hooks-removal-demo.html +++ b/demos/hooks-removal-demo.html @@ -9,90 +9,53 @@ diff --git a/demos/hooks-sanitize-css-demo.html b/demos/hooks-sanitize-css-demo.html index 5ebd354c5..a2b91fdf8 100644 --- a/demos/hooks-sanitize-css-demo.html +++ b/demos/hooks-sanitize-css-demo.html @@ -13,21 +13,21 @@ /* global DOMPurify */ 'use strict'; window.onload = function(){ - + // Specify dirty HTML - var dirty = document.getElementById('payload').value; + let dirty = document.getElementById('payload').value; // We can allow all (default elements) but SVG - var config = { + const config = { FORBID_TAGS: ['svg'] // SVG is not yet supported. Too messy. }; - // Specify CSS property allow-list - var allowed_properties = [ - 'color', + // Specify CSS property whitelist + const allowed_properties = [ + 'color', 'background', - 'border', - 'padding', + 'border', + 'padding', 'margin', 'font-family', 'content', @@ -35,48 +35,46 @@ ]; // Specify if CSS functions are permitted - var allow_css_functions = true; + const allow_css_functions = true; /** - * Take CSS property-value pairs and validate against allow-list, + * Take CSS property-value pairs and validate against white-list, * then add the styles to an array of property-value pairs */ function validateStyles(output, styles) { - // Validate regular CSS properties - for (var prop in styles) { - if (typeof styles[prop] === 'string') { - if (styles[prop] && allowed_properties.indexOf(prop) > -1) { - if (allow_css_functions || !/\w+\(/.test(styles[prop])) { - output.push(prop + ':' + styles[prop] +';'); - } - } - } + Object.keys(styles).forEach(function(index) { + if (styles.hasOwnProperty(index)) { + let normalizedKey = styles[index].replace(/([A-Z])/g, '-$1').toLowerCase(); + if (allowed_properties.includes(normalizedKey)) { + let value = styles[normalizedKey]; + output.push(`${normalizedKey}:${value};`); + } } + }); } /** * Take CSS rules and analyze them, create string wrapper to - * apply them to the DOM later on. Note that only selector rules + * apply them to the DOM later on. Note that only selector rules * are supported right now */ function addCSSRules(output, cssRules) { - for (var index = cssRules.length-1; index >= 0; index--) { - var rule = cssRules[index]; + Array.from(cssRules).reverse().forEach(rule => { // check for rules with selector - if (rule.type == 1 && rule.selectorText) { - output.push(rule.selectorText + '{') + if (rule.type === 1 && rule.selectorText) { + output.push(`${rule.selectorText}{`); if (rule.style) { - validateStyles(output, rule.style) + validateStyles(output, rule.style); } output.push('}'); } - } + }); } // Add a hook to enforce CSS element sanitization DOMPurify.addHook('uponSanitizeElement', function(node, data) { if (data.tagName === 'style') { - var output = []; + let output = []; addCSSRules(output, node.sheet.cssRules); node.textContent = output.join("\n"); } @@ -86,13 +84,13 @@ DOMPurify.addHook('afterSanitizeAttributes', function(node) { // Nasty hack to fix baseURI + CSS problems in Chrome if (!node.ownerDocument.baseURI) { - var base = document.createElement('base'); + let base = document.createElement('base'); base.href = document.baseURI; node.ownerDocument.head.appendChild(base); } // Check all style attribute values and validate them if (node.hasAttribute('style')) { - var output = []; + let output = []; validateStyles(output, node.style); // re-add styles in case any are left if (output.length) { @@ -104,7 +102,7 @@ }); // Clean HTML string and write into our DIV - var clean = DOMPurify.sanitize(dirty, config); + let clean = DOMPurify.sanitize(dirty, config); document.getElementById('sanitized').innerHTML = clean; } diff --git a/demos/hooks-scheme-allowlist.html b/demos/hooks-scheme-allowlist.html index 9f78fa1f3..8eb7411fc 100644 --- a/demos/hooks-scheme-allowlist.html +++ b/demos/hooks-scheme-allowlist.html @@ -9,68 +9,69 @@ diff --git a/demos/hooks-svg-demo.html b/demos/hooks-svg-demo.html deleted file mode 100644 index efb7dfd13..000000000 --- a/demos/hooks-svg-demo.html +++ /dev/null @@ -1,120 +0,0 @@ - - -
- - -
-