server-ts: Port services/html_sanitizer

This commit is contained in:
Elian Doran 2024-02-17 21:33:47 +02:00
parent 00c692cf28
commit dc22d05657
No known key found for this signature in database
11 changed files with 159 additions and 14 deletions

144
package-lock.json generated
View File

@ -96,6 +96,7 @@
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/node": "^20.11.19", "@types/node": "^20.11.19",
"@types/sanitize-html": "^2.11.0",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "25.9.8", "electron": "25.9.8",
@ -1646,6 +1647,89 @@
"@types/node": "*" "@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": { "node_modules/@types/send": {
"version": "0.17.4", "version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@ -16295,6 +16379,66 @@
"@types/node": "*" "@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": { "@types/send": {
"version": "0.17.4", "version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",

View File

@ -119,6 +119,7 @@
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/node": "^20.11.19", "@types/node": "^20.11.19",
"@types/sanitize-html": "^2.11.0",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "25.9.8", "electron": "25.9.8",

View File

@ -11,7 +11,7 @@ const ws = require('../../services/ws');
const log = require('../../services/log'); const log = require('../../services/log');
const utils = require('../../services/utils'); const utils = require('../../services/utils');
const path = require('path'); const path = require('path');
const htmlSanitizer = require('../../services/html_sanitizer.js'); const htmlSanitizer = require('../../services/html_sanitizer');
const {formatAttrForSearch} = require('../../services/attribute_formatter'); const {formatAttrForSearch} = require('../../services/attribute_formatter');
const jsdom = require("jsdom"); const jsdom = require("jsdom");
const { JSDOM } = jsdom; const { JSDOM } = jsdom;

View File

@ -1,18 +1,18 @@
const sanitizeHtml = require('sanitize-html'); import sanitizeHtml = require('sanitize-html');
const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl; import sanitizeUrl = require('@braintree/sanitize-url');
// intended mainly as protection against XSS via import // intended mainly as protection against XSS via import
// secondarily, it (partly) protects against "CSS takeover" // secondarily, it (partly) protects against "CSS takeover"
// sanitize also note titles, label values etc. - there are so many usages which make it difficult // sanitize also note titles, label values etc. - there are so many usages which make it difficult
// to guarantee all of them are properly handled // to guarantee all of them are properly handled
function sanitize(dirtyHtml) { function sanitize(dirtyHtml: string) {
if (!dirtyHtml) { if (!dirtyHtml) {
return dirtyHtml; return dirtyHtml;
} }
// avoid H1 per https://github.com/zadam/trilium/issues/1552 // avoid H1 per https://github.com/zadam/trilium/issues/1552
// demote H1, and if that conflicts with existing H2, demote that, etc // demote H1, and if that conflicts with existing H2, demote that, etc
const transformTags = {}; const transformTags: Record<string, string> = {};
const lowercasedHtml = dirtyHtml.toLowerCase(); const lowercasedHtml = dirtyHtml.toLowerCase();
for (let i = 1; i < 6; ++i) { for (let i = 1; i < 6; ++i) {
if (lowercasedHtml.includes(`<h${i}`)) { if (lowercasedHtml.includes(`<h${i}`)) {
@ -49,7 +49,7 @@ function sanitize(dirtyHtml) {
module.exports = { module.exports = {
sanitize, sanitize,
sanitizeUrl: url => { sanitizeUrl: (url: string) => {
return sanitizeUrl(url).trim(); return sanitizeUrl.sanitizeUrl(url).trim();
} }
}; };

View File

@ -11,7 +11,7 @@ const imageType = require('image-type');
const sanitizeFilename = require('sanitize-filename'); const sanitizeFilename = require('sanitize-filename');
const isSvg = require('is-svg'); const isSvg = require('is-svg');
const isAnimated = require('is-animated'); const isAnimated = require('is-animated');
const htmlSanitizer = require('./html_sanitizer.js'); const htmlSanitizer = require('./html_sanitizer');
async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { async function processImage(uploadBuffer, originalName, shrinkImageSwitch) {
const compressImages = optionService.getOptionBool("compressImages"); const compressImages = optionService.getOptionBool("compressImages");

View File

@ -7,7 +7,7 @@ const sql = require('../sql');
const noteService = require('../notes.js'); const noteService = require('../notes.js');
const imageService = require('../image.js'); const imageService = require('../image.js');
const protectedSessionService = require('../protected_session'); const protectedSessionService = require('../protected_session');
const htmlSanitizer = require('../html_sanitizer.js'); const htmlSanitizer = require('../html_sanitizer');
const {sanitizeAttributeName} = require('../sanitize_attribute_name'); const {sanitizeAttributeName} = require('../sanitize_attribute_name');
/** /**

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const marked = require("marked"); const marked = require("marked");
const htmlSanitizer = require('../html_sanitizer.js'); const htmlSanitizer = require('../html_sanitizer');
const importUtils = require('./utils'); const importUtils = require('./utils');
function renderToHtml(content, title) { function renderToHtml(content, title) {

View File

@ -3,7 +3,7 @@
const noteService = require('../../services/notes.js'); const noteService = require('../../services/notes.js');
const parseString = require('xml2js').parseString; const parseString = require('xml2js').parseString;
const protectedSessionService = require('../protected_session'); const protectedSessionService = require('../protected_session');
const htmlSanitizer = require('../html_sanitizer.js'); const htmlSanitizer = require('../html_sanitizer');
/** /**
* @param {TaskContext} taskContext * @param {TaskContext} taskContext

View File

@ -7,7 +7,7 @@ const markdownService = require('./markdown.js');
const mimeService = require('./mime.js'); const mimeService = require('./mime.js');
const utils = require('../../services/utils'); const utils = require('../../services/utils');
const importUtils = require('./utils'); const importUtils = require('./utils');
const htmlSanitizer = require('../html_sanitizer.js'); const htmlSanitizer = require('../html_sanitizer');
function importSingleFile(taskContext, file, parentNote) { function importSingleFile(taskContext, file, parentNote) {
const mime = mimeService.getMime(file.originalname) || file.mimetype; const mime = mimeService.getMime(file.originalname) || file.mimetype;

View File

@ -11,7 +11,7 @@ const protectedSessionService = require('../protected_session');
const mimeService = require('./mime.js'); const mimeService = require('./mime.js');
const treeService = require('../tree.js'); const treeService = require('../tree.js');
const yauzl = require("yauzl"); const yauzl = require("yauzl");
const htmlSanitizer = require('../html_sanitizer.js'); const htmlSanitizer = require('../html_sanitizer');
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const BAttachment = require('../../becca/entities/battachment'); const BAttachment = require('../../becca/entities/battachment');
const markdownService = require('./markdown.js'); const markdownService = require('./markdown.js');

View File

@ -17,7 +17,7 @@ const BNote = require('../becca/entities/bnote');
const BAttribute = require('../becca/entities/battribute'); const BAttribute = require('../becca/entities/battribute');
const BAttachment = require('../becca/entities/battachment'); const BAttachment = require('../becca/entities/battachment');
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const htmlSanitizer = require('./html_sanitizer.js'); const htmlSanitizer = require('./html_sanitizer');
const ValidationError = require('../errors/validation_error'); const ValidationError = require('../errors/validation_error');
const noteTypesService = require('./note_types'); const noteTypesService = require('./note_types');
const fs = require("fs"); const fs = require("fs");