dompurify 3.4.15 → 3.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
8
8
 
9
- 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.4.15**.
9
+ 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.4.16**.
10
10
 
11
11
  DOMPurify runs as 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.
12
12
 
@@ -81,6 +81,10 @@ const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
81
81
 
82
82
  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. See the [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model) for safe-usage recipes and the tags/attributes worth thinking twice about, and [Attack Classes & Bypass History](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History) for why post-processing and changing the markup context defeat sanitization.
83
83
 
84
+ ### What about passing a DOM node instead of a string?
85
+
86
+ `DOMPurify.sanitize()` also accepts a DOM node (an `Element`, `DocumentFragment` or `Document`). Since 3.4.14 that path is hardened for nodes that did not come out of the HTML parser: a node built with the DOM API or parsed as XML/XHTML (for example via `DOMParser` with `application/xhtml+xml` and `importNode()`) can carry case-preserved attribute names such as `ONERROR`, or a rawtext element like `<style>` with an element child or its own end tag inside its text. Both shapes are invisible to a string sanitizer because the HTML parser can never build them, but they break out on reparse. DOMPurify now removes attributes by their exact `Attr` node and treats these literal-text trees as unsafe, so mixing document contexts on the input side is covered. It remains your job not to mix contexts on the _output_ side, see the paragraph above.
87
+
84
88
  ### Okay, makes sense, let's move on
85
89
 
86
90
  After sanitizing your markup, you can also have a look at the property `DOMPurify.removed` and find out, what elements and attributes were thrown out. Please **do not use** this property for making any security critical decisions. This is just a little helper for curious minds.
@@ -376,10 +380,12 @@ const clean = DOMPurify.sanitize(dirty, { ALLOW_UNKNOWN_PROTOCOLS: true });
376
380
 
377
381
  // allow specific protocol handlers in URL attributes via regex (default is false, be careful, XSS risk)
378
382
  // by default only (protocol-)relative URLs, http, https, ftp, ftps, tel, mailto, callto, sms, cid, xmpp and matrix are allowed.
379
- // Default RegExp: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
383
+ // Default RegExp: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
384
+ // The example below extends the default with one additional scheme (sftp).
385
+ // Keep the pattern linear-time: it runs against attacker-controlled values.
380
386
  const clean = DOMPurify.sanitize(dirty, {
381
387
  ALLOWED_URI_REGEXP:
382
- /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
388
+ /^(?:(?:(?:f|ht)tps?|sftp|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
383
389
  });
384
390
  ```
385
391
 
@@ -461,6 +467,13 @@ dirty.setAttribute('href', 'javascript:alert(1)');
461
467
  const clean = DOMPurify.sanitize(dirty, { IN_PLACE: true }); // see https://github.com/cure53/DOMPurify/issues/288 for more info
462
468
  ```
463
469
 
470
+ A few things to know about `IN_PLACE`:
471
+
472
+ - The root node you pass in must itself be an allowed tag and must not be DOM-clobbered (for example a `<form>` with a child named `nodeName` or `ownerDocument`). If it is, DOMPurify strips the root's subtree of every non-allow-listed attribute and then throws a `TypeError`, so a rejected root is never handed back armed.
473
+ - If anything throws mid-walk, the same fail-closed neutralization runs over the root and over every subtree already detached during that walk before the error propagates.
474
+ - Nodes that a hook detaches from the tree (a common pattern, see [Hooks](#hooks)) are treated as removed. In `IN_PLACE` mode their subtree is neutralized inline, so an `<img onload>` that was already loading when you built the live tree cannot fire after `sanitize()` returns.
475
+ - DOMPurify cannot undo engine mutations that already fired _before_ `sanitize()` was called (a patch applied on connection, a `selectedcontent` re-clone, and so on). Sanitize attacker-controlled trees before connecting them to the live document, not after.
476
+
464
477
  There is even [more examples here](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this), showing how you can run, customize and configure DOMPurify to fit your needs.
465
478
 
466
479
  ## Persistent Configuration
@@ -496,6 +509,13 @@ DOMPurify.addHook(
496
509
  );
497
510
  ```
498
511
 
512
+ ### Hook behavior worth knowing
513
+
514
+ - **Detaching a node from a hook is supported.** If a `beforeSanitizeElements` or `uponSanitizeElement` hook removes the current node from the tree (for example `node.remove()` to drop a `foreignObject`), DOMPurify treats the node as removed and stops processing it. Such nodes are not recorded in `DOMPurify.removed`. In `IN_PLACE` mode the detached subtree is still neutralized (since 3.4.13), because a live node may carry an already-queued resource event.
515
+ - **`afterSanitizeElements` runs for kept custom elements, too.** Since 3.4.12, an element admitted via `CUSTOM_ELEMENT_HANDLING.tagNameCheck` goes through `afterSanitizeElements` exactly like an allow-listed element, so a policy applied in that hook (for example stripping an attribute from every surviving element) cannot silently skip custom elements ([GHSA-c2j3-45gr-mqc4](https://github.com/cure53/DOMPurify/security/advisories/GHSA-c2j3-45gr-mqc4)).
516
+ - **Prefer `hookEvent.keepAttr` / `forceKeepAttr` over writing to `hookEvent.allowedAttributes` or `allowedTags`.** The per-node flags cannot leak. Writes to the allow-list objects are isolated per call, including when the hook is installed lazily from inside another hook and when a persistent config from `setConfig()` is active ([GHSA-cmwh-pvxp-8882](https://github.com/cure53/DOMPurify/security/advisories/GHSA-cmwh-pvxp-8882)), but they remain the sharper tool.
517
+ - **`afterSanitize*` hooks run after validation.** Whatever you write there is not re-checked. Put attacker-influenced values through `uponSanitize*` hooks instead.
518
+
499
519
  ### A note on calling `sanitize()` from a hook
500
520
 
501
521
  **`DOMPurify.sanitize()` is not re-entrant.** Please do not call it from inside a hook, or from a configuration callback such as `CUSTOM_ELEMENT_HANDLING.tagNameCheck` or `attributeNameCheck`. Those callbacks run in the _middle_ of an active sanitizer pass.
@@ -526,7 +546,7 @@ We support `npm` officially. GitHub Actions workflow is configured to install de
526
546
 
527
547
  #### Scripts
528
548
 
529
- We use ESLint via `xo` as part of our pre-commit workflow to help ensure code consistency. In addition, we use [Prettier](https://github.com/prettier/prettier) for source and Markdown formatting, and `/dist` assets are built through `rollup`.
549
+ We use ESLint via `xo` as part of our pre-commit workflow to help ensure code consistency. In addition, we use [Prettier](https://github.com/prettier/prettier) for source and Markdown formatting, and `/dist` assets are built through `rolldown`.
530
550
 
531
551
  These are our npm scripts:
532
552
 
@@ -547,7 +567,7 @@ These are our npm scripts:
547
567
  - `npm run format:md` to only format Markdown files
548
568
  - `npm run build` to build type declarations and distribution bundles, then fix and clean up generated types
549
569
  - `npm run build:types` to only emit TypeScript declaration files
550
- - `npm run build:rollup` to build all Rollup bundles
570
+ - `npm run build:rolldown` to build all Rolldown bundles
551
571
  - `npm run build:umd` to only build an unminified UMD bundle
552
572
  - `npm run build:umd:min` to only build a minified UMD bundle
553
573
  - `npm run build:es` to only build the ES module bundle
@@ -573,4 +593,4 @@ Feature releases will not be announced to this list.
573
593
 
574
594
  Many people have helped DOMPurify become what it is today, and they deserve to be acknowledged!
575
595
 
576
- [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) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
596
+ [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) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)