Skip to content

Commit 1ba97fc

Browse files
committed
chore: update changelog and add tests for smuggled HTML in MathML/SVG handling
1 parent 22c1936 commit 1ba97fc

3 files changed

Lines changed: 30 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99
> - :house: [Internal]
1010
> - :nail_care: [Polish]
1111

12+
## 4.13.4
13+
14+
#### :bug: Bug Fix
15+
16+
- **Security / clean-html**: HTML smuggled into MathML/SVG (e.g. `<math><mglyph><html><body onload>`) is now stripped even when clean-html parses the fragment in a disconnected sandbox (`useIframeSandbox: false`). The guard used `isConnected`, which is `false` for a detached parse box, so the smuggled node slipped through; it now uses `box.contains()`. Fixes GHSA-rxcw-mc6f-6hr3 (mutation XSS via MathML/style rawtext carrier). Thanks @MatrixNeoKozak (#1380).
17+
- **safeHTML**: guard `location` access when neutralising `javascript:` links so `sanitizeHTMLElement` no longer throws a `ReferenceError` in SSR / Node environments where `location` is undefined (#1380).
18+
19+
#### :house: Internal
20+
21+
- **Dependencies**: security bumps for `tar` 7.5.16, `form-data` 4.0.6, `js-yaml` 4.3.0, `launch-editor` 2.14.1, `webpack-dev-server` 5.2.5 (+ `http-proxy-middleware` 2.0.9), `ws` 8.21.1 / `engine.io` 6.6.9 / `socket.io-adapter` 2.5.8, and `websocket-driver` 0.7.5 (Dependabot alerts #189–#197). All dev/test-only. (#1373, #1374, #1375, #1376, #1377, #1382, #1383)
22+
1223
## 4.13.3
1324

1425
#### :rocket: New Feature

src/core/helpers/html/safe-html.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function safeHTML(
7878
const foreign = box.querySelectorAll('math *, svg *');
7979

8080
for (let i = 0; i < foreign.length; i++) {
81-
if (foreign[i].isConnected && isSmuggledForeignHtml(foreign[i])) {
81+
if (box.contains(foreign[i]) && isSmuggledForeignHtml(foreign[i])) {
8282
Dom.safeRemove(foreign[i]);
8383
}
8484
}
@@ -219,7 +219,9 @@ export function sanitizeHTMLElement(
219219
// scheme (e.g. `java\tscript:`) — all of which the browser still resolves to
220220
// `javascript:` on click. See GHSA-j839-gqq4-gf9j.
221221
if (safeJavaScriptLink && href && isDangerousUrl(href, tagName)) {
222-
attr(elm, 'href', location.protocol + '//' + href);
222+
const protocol =
223+
typeof location !== 'undefined' ? location.protocol : 'http:';
224+
attr(elm, 'href', protocol + '//' + href);
223225
effected = true;
224226
}
225227

src/plugins/clean-html/clean-html.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,21 @@ describe('Clean html plugin', function () {
832832
});
833833
});
834834

835+
describe('smuggled HTML in MathML/SVG', function () {
836+
it('Should neutralize HTML smuggled into MathML or SVG when parsed in a disconnected fragment', function () {
837+
const editor = getJodit({
838+
cleanHTML: { useIframeSandbox: false }
839+
});
840+
editor.value =
841+
'<math><mglyph><html><body onload="alert(1)"></body></html></mglyph></math>';
842+
// The executable vector must be gone in every browser. Chrome strips the
843+
// smuggled `<body>`/`<html>` entirely; Firefox keeps them as inert
844+
// MathML-namespaced nodes without the handler — both are safe.
845+
expect(editor.value).does.not.contain('onload');
846+
expect(editor.value).does.not.contain('alert');
847+
});
848+
});
849+
835850
describe('denyTags (default: script,iframe,object,embed)', function () {
836851
['script', 'iframe', 'object', 'embed'].forEach(function (tag) {
837852
it('Should remove <' + tag + '> by default', function (done) {

0 commit comments

Comments
 (0)