diff --git a/package-lock.json b/package-lock.json index efaf46a..2174afb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2412,11 +2412,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "highlight.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.1.tgz", - "integrity": "sha512-jeW8rdPdhshYKObedYg5XGbpVgb1/DT4AHvDFXhkU7UnGSIjy9kkJ7zHG7qplhFHMitTSzh5/iClKQk3Kb2RFQ==" - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index 4bdc271..ebec6de 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "author": "", "license": "AGPL-3.0-only", "dependencies": { - "dompurify": "^2.2.0", - "highlight.js": "^10.3.1" + "dompurify": "^2.2.0" }, "devDependencies": { "@babel/core": "^7.11.1", diff --git a/src/js/events/components.js b/src/js/events/components.js deleted file mode 100644 index 2280fd6..0000000 --- a/src/js/events/components.js +++ /dev/null @@ -1,11 +0,0 @@ -const {ElemJS} = require("../basic") -const hljs = require("highlight.js") - -class HighlightedCode extends ElemJS { - constructor(code) { - super(code) - hljs.highlightBlock(this.element) - } -} - -module.exports = {HighlightedCode} diff --git a/src/js/events/event.js b/src/js/events/event.js index 765c03a..4ec6c54 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -44,9 +44,7 @@ class MatrixEvent extends ElemJS { if (this.editedAt) { this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) } - return this } - static canRender(_event) { return false } diff --git a/src/js/events/membership.js b/src/js/events/membership.js index f00a765..a81a3ca 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -6,7 +6,7 @@ function createMembershipEvent(membership, message) { return simpleEvent((e) => e.type == "m.room.member" && e.content.membership === membership, message) } -const JoinedEvent = createMembershipEvent("join", (e) => "joined the room") +const JoinedEvent = createMembershipEvent("join", (e) => {console.log(e); return "joined the room"}) const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) const LeaveEvent = createMembershipEvent("leave", () => "left the room") diff --git a/src/js/events/message.js b/src/js/events/message.js index 05ccfdd..c50ec57 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -1,17 +1,21 @@ -const {ejs, ElemJS} = require("../basic") -const hljs = require("highlight.js") -const {HighlightedCode} = require("./components") +const {ejs} = require("../basic") const DOMPurify = require("dompurify") const {resolveMxc} = require("../functions") const {MatrixEvent} = require("./event") const purifier = DOMPurify() -purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { - //If purifier already rejected an attribute there is no point in checking it - if (hookevent.keepAttr === false) return; +purifier.setConfig({ + ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris + ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], - const allowedElementAttributes = { + // In case we mess up none of those attributes should read to XSS + ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", + "name", "target", "href", + "width", "height", "alt", "title", "src", "data-mx-emoticon", + "start", "class"], + //Custom config option that allows the array of attributes for a given tag + ALLOWED_ATTR_CUSTOM: { "FONT": ["data-mx-bg-color", "data-mx-color", "color"], "SPAN": ["data-mx-bg-color", "data-mx-color", "color"], "A": ["name", "target", "href"], @@ -19,73 +23,58 @@ purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { "OL": ["start"], "CODE": ["class"], } +}) - const allowed_attributes = allowedElementAttributes[node.tagName] || [] +//Handle our custom tag +purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { + //If purifier already rejected an attribute there is no point in checking it + if (hookevent.keepAttr === false) return; + + const allowed_attributes = config.ALLOWED_ATTR_CUSTOM[node.tagName] || [] hookevent.keepAttr = allowed_attributes.indexOf(hookevent.attrName) > -1; }) //Remove bad classes from our code element purifier.addHook("uponSanitizeElement", (node, hookevent, config) => { - if (node.tagName == "CODE") { - node.classList.forEach(c => { - if (!c.startsWith("language-")) { - node.classList.remove(c) - } - }) - } - if (node.tagName == "A") { + if (node.tagName != "CODE") return + node.classList.forEach(c => { + if (!c.startsWith("language-")) { + node.classList.remove(c) + } + }) + return node +}) + +purifier.addHook("afterSanitizeAttributes", (node, hookevent, config) => { + if (node.tagName == "IMG") { + let src = node.getAttribute("src") + if (src) src = resolveMxc(src) + + node.setAttribute("src", src) + } else if (node.tagName == "A") { node.setAttribute("rel", "noopener") + } else if (node.tagName == "FONT" || node.tagName == "SPAN") { + const color = node.getAttribute("data-mx-color") + const bgColor = node.getAttribute("data-mx-bg-color") + if (color) node.style.color = color; + if (bgColor) node.style.backgroundColor = bgColor; } return node }) -function cleanHTML(html) { - const config = { - ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris - ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], - // In case we mess up in the uponSanitizeAttribute hook - ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", - "name", "target", "href", - "width", "height", "alt", "title", "src", "data-mx-emoticon", - "start", "class"], - } +function sanitize(html) { return purifier.sanitize(html) } -//Here we put all the processing of the messages that isn't as likely to potentially lead to security issues -function postProcessElements(rootNode) { - const element = rootNode.element - element.querySelectorAll("code").forEach((n) => rootNode.child(new HighlightedCode(n))) - - element.querySelectorAll("img").forEach((n) => { - let src = n.getAttribute("src") - if (src) src = resolveMxc(src) - n.setAttribute("src", src) - }) - element.querySelectorAll("font, span").forEach((n) => { - const color = n.getAttribute("data-mx-color") || n.getAttribute("color") - const bgColor = n.getAttribute("data-mx-bg-color") - if (color) n.style.color = color; - if (bgColor) n.style.backgroundColor = bgColor; - }) -} - - class HTMLMessage extends MatrixEvent { render() { - this.clearChildren() - + super.render() let html = this.data.content.formatted_body const content = ejs("div") - - html = cleanHTML(html) + html = sanitize(html) content.html(html) - postProcessElements(content) - this.child(content) - - super.render() } static canRender(event) { @@ -102,8 +91,8 @@ class HTMLMessage extends MatrixEvent { class TextMessage extends MatrixEvent { render() { - this.text(this.data.content.body) - return super.render() + super.render() + return this.text(this.data.content.body) } static canRender(event) { diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index 3e3bbd6..bbc40f8 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,3 +1,4 @@ const {simpleEvent} = require("./event") const UnknownEvent = simpleEvent(() => true, () => "Cannot render event") +console.log(UnknownEvent) module.exports = [UnknownEvent] diff --git a/src/sass/components/highlighted-code.sass b/src/sass/components/highlighted-code.sass deleted file mode 100644 index 1ec7290..0000000 --- a/src/sass/components/highlighted-code.sass +++ /dev/null @@ -1 +0,0 @@ -@use "../../../node_modules/highlight.js/scss/obsidian" diff --git a/src/sass/main.sass b/src/sass/main.sass index 24cd6ca..150af73 100644 --- a/src/sass/main.sass +++ b/src/sass/main.sass @@ -5,5 +5,4 @@ @use "./components/chat" @use "./components/chat-input" @use "./components/anchor" -@use "./components/highlighted-code" @use "./loading"