From 4083ca8e928a9844f46c7def91d1b8d24f7189ac Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 14:49:04 +0100 Subject: [PATCH 1/2] fix: render markdown in chat assistant messages (#518) Add marked + DOMPurify to AutomationChatView so assistant and system messages render formatted HTML instead of raw markdown tokens. User messages remain plain-text interpolation to avoid any v-html XSS path for user-supplied content. --- frontend/taskdeck-web/package-lock.json | 41 +++++++ frontend/taskdeck-web/package.json | 3 + .../src/views/AutomationChatView.vue | 111 +++++++++++++++++- 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/frontend/taskdeck-web/package-lock.json b/frontend/taskdeck-web/package-lock.json index 6f809a15d..c164dc554 100644 --- a/frontend/taskdeck-web/package-lock.json +++ b/frontend/taskdeck-web/package-lock.json @@ -11,6 +11,8 @@ "@microsoft/signalr": "^10.0.0", "@tanstack/vue-virtual": "^3.13.23", "axios": "^1.13.2", + "dompurify": "^3.3.3", + "marked": "^17.0.5", "pinia": "^3.0.4", "vue": "^3.5.24", "vue-router": "^4.6.3", @@ -18,6 +20,7 @@ }, "devDependencies": { "@playwright/test": "^1.56.1", + "@types/dompurify": "^3.0.5", "@types/node": "^24.10.0", "@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/parser": "^8.46.2", @@ -1223,6 +1226,16 @@ "vue": "^2.7.0 || ^3.0.0" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -1247,6 +1260,13 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", @@ -2514,6 +2534,15 @@ "node": ">=6.0.0" } }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3881,6 +3910,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.5.tgz", + "integrity": "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/frontend/taskdeck-web/package.json b/frontend/taskdeck-web/package.json index dd4c0ec1a..62e5252ae 100644 --- a/frontend/taskdeck-web/package.json +++ b/frontend/taskdeck-web/package.json @@ -32,6 +32,8 @@ "@microsoft/signalr": "^10.0.0", "@tanstack/vue-virtual": "^3.13.23", "axios": "^1.13.2", + "dompurify": "^3.3.3", + "marked": "^17.0.5", "pinia": "^3.0.4", "vue": "^3.5.24", "vue-router": "^4.6.3", @@ -39,6 +41,7 @@ }, "devDependencies": { "@playwright/test": "^1.56.1", + "@types/dompurify": "^3.0.5", "@types/node": "^24.10.0", "@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/parser": "^8.46.2", diff --git a/frontend/taskdeck-web/src/views/AutomationChatView.vue b/frontend/taskdeck-web/src/views/AutomationChatView.vue index 3762b1ecc..64489199c 100644 --- a/frontend/taskdeck-web/src/views/AutomationChatView.vue +++ b/frontend/taskdeck-web/src/views/AutomationChatView.vue @@ -1,10 +1,12 @@