diff --git a/package.json b/package.json index 1ce1353d5..76d1cd9a7 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "bootstrap-icons": "^1.10.5", "codemirror": "^5.63.1", "d3": "^7.0.0", + "dompurify": "^3.2.5", "fabric": "^5.3.0", "hammerjs": "^2.0.8", "intl-pluralrules": "^2.0.0", diff --git a/ts/editor/NoteEditor.svelte b/ts/editor/NoteEditor.svelte index 9bf66d858..a51ef4fe9 100644 --- a/ts/editor/NoteEditor.svelte +++ b/ts/editor/NoteEditor.svelte @@ -133,7 +133,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } for (const [index, [, fieldContent]] of fs.entries()) { - fieldStores[index].set(fieldContent); + fieldStores[index].set(sanitize(fieldContent)); } fieldNames = newFieldNames; @@ -424,6 +424,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } from "../routes/image-occlusion/store"; import CollapseLabel from "./CollapseLabel.svelte"; import * as oldEditorAdapter from "./old-editor-adapter"; + import { sanitize } from "$lib/domlib"; $: isIOImageLoaded = false; $: ioImageLoadedStore.set(isIOImageLoaded); diff --git a/ts/lib/domlib/index.ts b/ts/lib/domlib/index.ts index 27b21016d..0834e0e77 100644 --- a/ts/lib/domlib/index.ts +++ b/ts/lib/domlib/index.ts @@ -5,4 +5,5 @@ export * from "./content-editable"; export * from "./location"; export * from "./move-nodes"; export * from "./place-caret"; +export * from "./sanitize"; export * from "./surround"; diff --git a/ts/lib/domlib/sanitize.ts b/ts/lib/domlib/sanitize.ts new file mode 100644 index 000000000..d6ae4f049 --- /dev/null +++ b/ts/lib/domlib/sanitize.ts @@ -0,0 +1,10 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import DOMPurify from "dompurify"; + +export function sanitize(html: string): string { + // We need to treat the text as a document fragment, or a style tag + // at the start of input will be discarded. + return DOMPurify.sanitize(html, { FORCE_BODY: true }); +} diff --git a/ts/licenses.json b/ts/licenses.json index 9d6f7d2c1..58815d397 100644 --- a/ts/licenses.json +++ b/ts/licenses.json @@ -57,6 +57,12 @@ "path": "node_modules/@tootallnate/once", "licenseFile": "node_modules/@tootallnate/once/LICENSE" }, + "@types/trusted-types@2.0.7": { + "licenses": "MIT", + "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "path": "node_modules/@types/trusted-types", + "licenseFile": "node_modules/@types/trusted-types/LICENSE" + }, "abab@2.0.6": { "licenses": "BSD-3-Clause", "repository": "https://github.com/jsdom/abab", @@ -95,8 +101,8 @@ "repository": "https://github.com/TooTallNate/node-agent-base", "publisher": "Nathan Rajlich", "email": "nathan@tootallnate.net", - "path": "node_modules/agent-base", - "licenseFile": "node_modules/agent-base/README.md" + "path": "node_modules/http-proxy-agent/node_modules/agent-base", + "licenseFile": "node_modules/http-proxy-agent/node_modules/agent-base/README.md" }, "asynckit@0.4.0": { "licenses": "MIT", @@ -436,6 +442,14 @@ "path": "node_modules/domexception", "licenseFile": "node_modules/domexception/LICENSE.txt" }, + "dompurify@3.2.5": { + "licenses": "(MPL-2.0 OR Apache-2.0)", + "repository": "https://github.com/cure53/DOMPurify", + "publisher": "Dr.-Ing. Mario Heiderich, Cure53", + "email": "mario@cure53.de", + "path": "node_modules/dompurify", + "licenseFile": "node_modules/dompurify/LICENSE" + }, "empty-npm-package@1.0.0": { "licenses": "ISC", "path": "node_modules/canvas" @@ -572,6 +586,14 @@ "path": "node_modules/lodash-es", "licenseFile": "node_modules/lodash-es/LICENSE" }, + "lru-cache@10.4.3": { + "licenses": "ISC", + "repository": "https://github.com/isaacs/node-lru-cache", + "publisher": "Isaac Z. Schlueter", + "email": "i@izs.me", + "path": "node_modules/lru-cache", + "licenseFile": "node_modules/lru-cache/LICENSE" + }, "marked@5.1.2": { "licenses": "MIT", "repository": "https://github.com/markedjs/marked", @@ -768,16 +790,16 @@ "repository": "https://github.com/jsdom/whatwg-url", "publisher": "Sebastian Mayr", "email": "github@smayr.name", - "path": "node_modules/whatwg-url", - "licenseFile": "node_modules/whatwg-url/LICENSE.txt" + "path": "node_modules/jsdom/node_modules/whatwg-url", + "licenseFile": "node_modules/jsdom/node_modules/whatwg-url/LICENSE.txt" }, "whatwg-url@11.0.0": { "licenses": "MIT", "repository": "https://github.com/jsdom/whatwg-url", "publisher": "Sebastian Mayr", "email": "github@smayr.name", - "path": "node_modules/data-urls/node_modules/whatwg-url", - "licenseFile": "node_modules/data-urls/node_modules/whatwg-url/LICENSE.txt" + "path": "node_modules/whatwg-url", + "licenseFile": "node_modules/whatwg-url/LICENSE.txt" }, "ws@8.18.0": { "licenses": "MIT", diff --git a/yarn.lock b/yarn.lock index a1ee4eed6..d52290ab2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1662,6 +1662,13 @@ __metadata: languageName: node linkType: hard +"@types/trusted-types@npm:^2.0.7": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 10c0/4c4855f10de7c6c135e0d32ce462419d8abbbc33713b31d294596c0cc34ae1fa6112a2f9da729c8f7a20707782b0d69da3b1f8df6645b0366d08825ca1522e0c + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^5.60.1": version: 5.62.0 resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" @@ -2018,6 +2025,7 @@ __metadata: cross-env: "npm:^7.0.2" d3: "npm:^7.0.0" diff: "npm:^5.0.0" + dompurify: "npm:^3.2.5" dprint: "npm:^0.47.2" esbuild: "npm:^0.25.0" esbuild-sass-plugin: "npm:^2" @@ -3169,6 +3177,18 @@ __metadata: languageName: node linkType: hard +"dompurify@npm:^3.2.5": + version: 3.2.5 + resolution: "dompurify@npm:3.2.5" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10c0/b564167cc588933ad2d25c185296716bdd7124e9d2a75dac76efea831bb22d1230ce5205a1ab6ce4c1010bb32ac35f7a5cb2dd16c78cbf382111f1228362aa59 + languageName: node + linkType: hard + "domutils@npm:^3.0.1": version: 3.1.0 resolution: "domutils@npm:3.1.0"