diff --git a/package-lock.json b/package-lock.json index c44652683..0a8e99442 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,7 @@ "@types/jsdom": "^21.1.6", "@types/mime-types": "^2.1.4", "@types/node": "^20.11.19", + "@types/sanitize-html": "^2.11.0", "@types/ws": "^8.5.10", "cross-env": "7.0.3", "electron": "25.9.8", @@ -1646,6 +1647,89 @@ "@types/node": "*" } }, + "node_modules/@types/sanitize-html": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", + "integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==", + "dev": true, + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, + "node_modules/@types/sanitize-html/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -16295,6 +16379,66 @@ "@types/node": "*" } }, + "@types/sanitize-html": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", + "integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==", + "dev": true, + "requires": { + "htmlparser2": "^8.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + } + } + }, "@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", diff --git a/package.json b/package.json index 0f7f019ea..9c962217e 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "@types/jsdom": "^21.1.6", "@types/mime-types": "^2.1.4", "@types/node": "^20.11.19", + "@types/sanitize-html": "^2.11.0", "@types/ws": "^8.5.10", "cross-env": "7.0.3", "electron": "25.9.8", diff --git a/src/routes/api/clipper.js b/src/routes/api/clipper.js index 52c7ccea2..1b8bee19b 100644 --- a/src/routes/api/clipper.js +++ b/src/routes/api/clipper.js @@ -11,7 +11,7 @@ const ws = require('../../services/ws'); const log = require('../../services/log'); const utils = require('../../services/utils'); const path = require('path'); -const htmlSanitizer = require('../../services/html_sanitizer.js'); +const htmlSanitizer = require('../../services/html_sanitizer'); const {formatAttrForSearch} = require('../../services/attribute_formatter'); const jsdom = require("jsdom"); const { JSDOM } = jsdom; diff --git a/src/services/html_sanitizer.js b/src/services/html_sanitizer.ts similarity index 88% rename from src/services/html_sanitizer.js rename to src/services/html_sanitizer.ts index 99bb567ad..6e7d8b537 100644 --- a/src/services/html_sanitizer.js +++ b/src/services/html_sanitizer.ts @@ -1,18 +1,18 @@ -const sanitizeHtml = require('sanitize-html'); -const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl; +import sanitizeHtml = require('sanitize-html'); +import sanitizeUrl = require('@braintree/sanitize-url'); // intended mainly as protection against XSS via import // secondarily, it (partly) protects against "CSS takeover" // sanitize also note titles, label values etc. - there are so many usages which make it difficult // to guarantee all of them are properly handled -function sanitize(dirtyHtml) { +function sanitize(dirtyHtml: string) { if (!dirtyHtml) { return dirtyHtml; } // avoid H1 per https://github.com/zadam/trilium/issues/1552 // demote H1, and if that conflicts with existing H2, demote that, etc - const transformTags = {}; + const transformTags: Record = {}; const lowercasedHtml = dirtyHtml.toLowerCase(); for (let i = 1; i < 6; ++i) { if (lowercasedHtml.includes(` { - return sanitizeUrl(url).trim(); + sanitizeUrl: (url: string) => { + return sanitizeUrl.sanitizeUrl(url).trim(); } }; diff --git a/src/services/image.js b/src/services/image.js index 04add20f8..8d7aec5c8 100644 --- a/src/services/image.js +++ b/src/services/image.js @@ -11,7 +11,7 @@ const imageType = require('image-type'); const sanitizeFilename = require('sanitize-filename'); const isSvg = require('is-svg'); const isAnimated = require('is-animated'); -const htmlSanitizer = require('./html_sanitizer.js'); +const htmlSanitizer = require('./html_sanitizer'); async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { const compressImages = optionService.getOptionBool("compressImages"); diff --git a/src/services/import/enex.js b/src/services/import/enex.js index 7cc8973ce..80903d9f6 100644 --- a/src/services/import/enex.js +++ b/src/services/import/enex.js @@ -7,7 +7,7 @@ const sql = require('../sql'); const noteService = require('../notes.js'); const imageService = require('../image.js'); const protectedSessionService = require('../protected_session'); -const htmlSanitizer = require('../html_sanitizer.js'); +const htmlSanitizer = require('../html_sanitizer'); const {sanitizeAttributeName} = require('../sanitize_attribute_name'); /** diff --git a/src/services/import/markdown.js b/src/services/import/markdown.js index 7cdd6b3d2..745f30847 100644 --- a/src/services/import/markdown.js +++ b/src/services/import/markdown.js @@ -1,7 +1,7 @@ "use strict"; const marked = require("marked"); -const htmlSanitizer = require('../html_sanitizer.js'); +const htmlSanitizer = require('../html_sanitizer'); const importUtils = require('./utils'); function renderToHtml(content, title) { diff --git a/src/services/import/opml.js b/src/services/import/opml.js index a547bf7ad..f71b41fbd 100644 --- a/src/services/import/opml.js +++ b/src/services/import/opml.js @@ -3,7 +3,7 @@ const noteService = require('../../services/notes.js'); const parseString = require('xml2js').parseString; const protectedSessionService = require('../protected_session'); -const htmlSanitizer = require('../html_sanitizer.js'); +const htmlSanitizer = require('../html_sanitizer'); /** * @param {TaskContext} taskContext diff --git a/src/services/import/single.js b/src/services/import/single.js index 5e7c92630..b516a50e9 100644 --- a/src/services/import/single.js +++ b/src/services/import/single.js @@ -7,7 +7,7 @@ const markdownService = require('./markdown.js'); const mimeService = require('./mime.js'); const utils = require('../../services/utils'); const importUtils = require('./utils'); -const htmlSanitizer = require('../html_sanitizer.js'); +const htmlSanitizer = require('../html_sanitizer'); function importSingleFile(taskContext, file, parentNote) { const mime = mimeService.getMime(file.originalname) || file.mimetype; diff --git a/src/services/import/zip.js b/src/services/import/zip.js index cf6e870ac..db8565037 100644 --- a/src/services/import/zip.js +++ b/src/services/import/zip.js @@ -11,7 +11,7 @@ const protectedSessionService = require('../protected_session'); const mimeService = require('./mime.js'); const treeService = require('../tree.js'); const yauzl = require("yauzl"); -const htmlSanitizer = require('../html_sanitizer.js'); +const htmlSanitizer = require('../html_sanitizer'); const becca = require('../../becca/becca'); const BAttachment = require('../../becca/entities/battachment'); const markdownService = require('./markdown.js'); diff --git a/src/services/notes.js b/src/services/notes.js index fe6e84322..ae60c16ad 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -17,7 +17,7 @@ const BNote = require('../becca/entities/bnote'); const BAttribute = require('../becca/entities/battribute'); const BAttachment = require('../becca/entities/battachment'); const dayjs = require("dayjs"); -const htmlSanitizer = require('./html_sanitizer.js'); +const htmlSanitizer = require('./html_sanitizer'); const ValidationError = require('../errors/validation_error'); const noteTypesService = require('./note_types'); const fs = require("fs");