From 7b18c325dadad5b1c7d2d0dd02d68800e5089b52 Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Wed, 3 Dec 2025 09:33:53 +0100 Subject: [PATCH] fix: cross site scripting with html content --- apps/dashboard/package.json | 2 + apps/dashboard/src/app.html | 4 +- .../lib/components/CodeSnippet/index.svelte | 5 +- .../Lesson/Exercise/UpdateDescription.svelte | 17 +- .../Lesson/Exercise/ViewMode.svelte | 3 +- .../Lesson/LessonVersionHistory.svelte | 21 +- .../Materials/components/ComponentNote.svelte | 3 +- .../components/NewsFeed/NewsFeedCard.svelte | 33 +-- .../components/HTMLRender/HTMLRender.svelte | 3 +- .../src/lib/utils/functions/sanitize.ts | 177 ++++++++++++++++ .../src/routes/csp-report/+server.ts | 26 +++ .../routes/lms/community/[slug]/+page.svelte | 42 ++-- .../org/[slug]/community/[slug]/+page.svelte | 5 +- apps/dashboard/svelte.config.js | 72 +++++++ pnpm-lock.yaml | 196 ++++++++++-------- 15 files changed, 458 insertions(+), 151 deletions(-) create mode 100644 apps/dashboard/src/lib/utils/functions/sanitize.ts create mode 100644 apps/dashboard/src/routes/csp-report/+server.ts diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 4dc041ec..ef0e8c65 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -42,6 +42,7 @@ "d3-cloud": "^1.2.7", "d3-sankey": "^0.12.3", "dayjs": "^1.11.10", + "dompurify": "^3.3.0", "encoding": "^0.1.13", "hotkeys-js": "^3.11.2", "html-to-image": "^1.11.11", @@ -78,6 +79,7 @@ "@tailwindcss/typography": "^0.5.9", "@testing-library/jest-dom": "^6.1.5", "@testing-library/svelte": "^4.0.5", + "@types/dompurify": "^3.2.0", "@types/jest": "^29.5.10", "@types/lodash": "^4.14.196", "all-object-keys": "^2.2.0", diff --git a/apps/dashboard/src/app.html b/apps/dashboard/src/app.html index 8ee54266..0a09b997 100644 --- a/apps/dashboard/src/app.html +++ b/apps/dashboard/src/app.html @@ -17,10 +17,10 @@ - - + %sveltekit.head% diff --git a/apps/dashboard/src/lib/components/CodeSnippet/index.svelte b/apps/dashboard/src/lib/components/CodeSnippet/index.svelte index 7e441b3d..09ae2112 100644 --- a/apps/dashboard/src/lib/components/CodeSnippet/index.svelte +++ b/apps/dashboard/src/lib/components/CodeSnippet/index.svelte @@ -1,7 +1,8 @@ -
- {@html code} +
+ {@html sanitizeHtml(code)}
diff --git a/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/UpdateDescription.svelte b/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/UpdateDescription.svelte index dddd545a..cb0c3390 100644 --- a/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/UpdateDescription.svelte +++ b/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/UpdateDescription.svelte @@ -4,6 +4,7 @@ import TextField from '$lib/components/Form/TextField.svelte'; import { questionnaire } from '../store/exercise'; import TextEditor from '$lib/components/TextEditor/index.svelte'; + import { sanitizeHtml } from '$lib/utils/functions/sanitize'; import { t } from '$lib/utils/functions/translations'; export let preview: boolean; @@ -52,31 +53,33 @@ {:else if preview}

{$questionnaire.title}

-

+

{$questionnaire.questions.length} {$t('course.navItem.lessons.exercises.all_exercises.view_mode.questions')}

| -

+

{getTotalPossibleGrade($questionnaire.questions)} {$t('course.navItem.lessons.exercises.all_exercises.view_mode.points')}.

| -

+

{$t('course.navItem.lessons.exercises.all_exercises.view_mode.all')}

{#if $questionnaire.due_by} | -

+

{$t('course.navItem.lessons.exercises.all_exercises.view_mode.due')}: {new Date($questionnaire.due_by).toLocaleString()}

{/if}
-
- {@html $questionnaire.description || - $t('course.navItem.lessons.exercises.all_exercises.description.no')} +
+ {@html sanitizeHtml( + $questionnaire.description || + $t('course.navItem.lessons.exercises.all_exercises.description.no') + )}
{/if} diff --git a/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/ViewMode.svelte b/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/ViewMode.svelte index de02e73a..c8a8c1a9 100644 --- a/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/ViewMode.svelte +++ b/apps/dashboard/src/lib/components/Course/components/Lesson/Exercise/ViewMode.svelte @@ -27,6 +27,7 @@ import { lesson } from '../store/lessons'; import { browser } from '$app/environment'; import { COURSE_TYPE } from '$lib/utils/types'; + import { sanitizeHtml } from '$lib/utils/functions/sanitize'; import { t } from '$lib/utils/functions/translations'; export let preview: boolean = false; @@ -296,7 +297,7 @@
- {@html $questionnaire.description || 'No desription'} + {@html sanitizeHtml($questionnaire.description || 'No desription')}
-
+
@@ -180,10 +181,10 @@
{/if}
-
+
-

+

{lessonTitle}

@@ -193,7 +194,7 @@
- {@html content} + {@html sanitizeHtml(content)}
@@ -203,9 +204,9 @@
-

+

{$t('course.navItem.lessons.version_history.title')}

@@ -213,25 +214,25 @@ {#each lessonHistory as version, index} {/each} -
+
{$t('course.navItem.lessons.version_history.fetch_more_versions')} diff --git a/apps/dashboard/src/lib/components/Course/components/Lesson/Materials/components/ComponentNote.svelte b/apps/dashboard/src/lib/components/Course/components/Lesson/Materials/components/ComponentNote.svelte index a93ef636..a1de0457 100644 --- a/apps/dashboard/src/lib/components/Course/components/Lesson/Materials/components/ComponentNote.svelte +++ b/apps/dashboard/src/lib/components/Course/components/Lesson/Materials/components/ComponentNote.svelte @@ -1,5 +1,6 @@