From 059869e8fd9a0dc805f0995109a07b12ff9cbd1a Mon Sep 17 00:00:00 2001 From: "LAPTOP-CTLLJBUM\\man_l" Date: Thu, 25 Aug 2022 17:15:42 +0100 Subject: [PATCH 1/3] DLSV2-637 Sanitise html strings submitted to database using Jodit editor throughout solution --- .../FrameworksController/AssessmentQuestions.cs | 4 +++- .../FrameworksController/Competencies.cs | 10 +++++++--- .../FrameworksController/Frameworks.cs | 3 +++ .../DigitalLearningSolutions.Web.csproj | 1 + .../Helpers/SanitizerHelper.cs | 15 +++++++++++++++ .../Scripts/frameworks/htmleditor.ts | 9 +++++++-- DigitalLearningSolutions.Web/package.json | 2 ++ 7 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 DigitalLearningSolutions.Web/Helpers/SanitizerHelper.cs diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/AssessmentQuestions.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/AssessmentQuestions.cs index 7a7b6c0194..fbdcd33553 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/AssessmentQuestions.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/AssessmentQuestions.cs @@ -9,6 +9,7 @@ using DigitalLearningSolutions.Data.Models.SessionData.Frameworks; using System.Collections.Generic; using DigitalLearningSolutions.Data.Enums; + using DigitalLearningSolutions.Web.Helpers; public partial class FrameworksController { @@ -430,7 +431,8 @@ public IActionResult EditAssessmentQuestionOptions(AssessmentQuestionDetail asse if (!ModelState.IsValid) { return RedirectToAction("EditAssessmentQuestionOptions", "Frameworks", new { frameworkId, assessmentQuestionId, frameworkCompetencyId }); - } + } + assessmentQuestionDetail.ScoringInstructions = SanitizerHelper.SanitizeHtmlData(assessmentQuestionDetail.ScoringInstructions); SessionAssessmentQuestion sessionAssessmentQuestion = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.EditAssessmentQuestion, TempData diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs index b9b6190a69..88df37b53e 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs @@ -1,12 +1,14 @@ using DigitalLearningSolutions.Data.Models.Frameworks; using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate; using DigitalLearningSolutions.Data.Models.SelfAssessments; +using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.ViewModels.Frameworks; using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Logging; using System.Linq; +using System.Web; namespace DigitalLearningSolutions.Web.Controllers.FrameworksController { @@ -63,10 +65,11 @@ public IActionResult AddEditFrameworkCompetencyGroup(int frameworkId, Competency if (userRole < 2) return StatusCode(403); if (competencyGroupBase.ID > 0) { - frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name, competencyGroupBase.Description, adminId); + frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData + (competencyGroupBase.Description), adminId); return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId }) + "#fcgroup-" + frameworkCompetencyGroupId.ToString()); } - var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, competencyGroupBase.Description, adminId); + var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId); if (newCompetencyGroupId > 0) { var newFrameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminId); @@ -131,7 +134,8 @@ public IActionResult AddEditFrameworkCompetency(int frameworkId, int? frameworkC [Route("/Frameworks/{frameworkId}/Competency/{frameworkCompetencyGroupId}")] [Route("/Frameworks/{frameworkId}/Competency/")] public IActionResult AddEditFrameworkCompetency(int frameworkId, FrameworkCompetency frameworkCompetency, int? frameworkCompetencyGroupId, int frameworkCompetencyId = 0, int[] selectedFlagIds = null) - { + { + frameworkCompetency.Description = SanitizerHelper.SanitizeHtmlData(frameworkCompetency.Description); if (!ModelState.IsValid) { ModelState.Remove(nameof(FrameworkCompetency.Name)); diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs index c0ce5b3504..12d37c4902 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs @@ -14,6 +14,7 @@ namespace DigitalLearningSolutions.Web.Controllers.FrameworksController using DigitalLearningSolutions.Data.Helpers; using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate; using DigitalLearningSolutions.Web.Attributes; + using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.Models.Enums; using DigitalLearningSolutions.Web.ViewModels.Common; using System.Net; @@ -335,6 +336,7 @@ public IActionResult FrameworkDescription(DetailFramework detailFramework, strin } return RedirectToAction("FrameworkType", "Frameworks", new { actionname }); } + detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description); frameworkService.UpdateFrameworkDescription(frameworkId, GetAdminId(), detailFramework.Description); return RedirectToAction("ViewFramework", new { tabname = "Details", frameworkId }); @@ -739,6 +741,7 @@ public IActionResult InsertFramework() logger.LogWarning($"Failed to create framework: adminId: {adminId}"); return StatusCode(500); } + detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description); var newFramework = frameworkService.CreateFramework(detailFramework, adminId); TempData.Clear(); return RedirectToAction("AddCollaborators", "Frameworks", new { actionname = "New", frameworkId = newFramework.ID }); diff --git a/DigitalLearningSolutions.Web/DigitalLearningSolutions.Web.csproj b/DigitalLearningSolutions.Web/DigitalLearningSolutions.Web.csproj index 30c8ba7087..9689a7747e 100644 --- a/DigitalLearningSolutions.Web/DigitalLearningSolutions.Web.csproj +++ b/DigitalLearningSolutions.Web/DigitalLearningSolutions.Web.csproj @@ -61,6 +61,7 @@ + diff --git a/DigitalLearningSolutions.Web/Helpers/SanitizerHelper.cs b/DigitalLearningSolutions.Web/Helpers/SanitizerHelper.cs new file mode 100644 index 0000000000..0b6dc1a1a7 --- /dev/null +++ b/DigitalLearningSolutions.Web/Helpers/SanitizerHelper.cs @@ -0,0 +1,15 @@ +namespace DigitalLearningSolutions.Web.Helpers +{ + using Ganss.XSS; + public static class SanitizerHelper + { + public static string SanitizeHtmlData(string htmlData) + { + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedTags.Remove("iframe"); + sanitizer.AllowedTags.Remove("img"); + var sanitized = sanitizer.Sanitize(htmlData); + return sanitized; + } + } +} diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts index 0caaf6701c..3289462a44 100644 --- a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts @@ -1,8 +1,9 @@ import { Jodit } from 'jodit'; +import DOMPurify from 'dompurify'; let jodited = false; if (jodited === false) { - const editor = Jodit.make('.html-editor', { + const editor = Jodit.make(".html-editor", { buttons: [ 'source', '|', 'bold', @@ -57,7 +58,7 @@ if (jodited === false) { 'ul', 'ol', '|', 'undo', 'redo', - ], + ], style: { backgroundColor: '#FFF', }, @@ -65,5 +66,9 @@ if (jodited === false) { if (editor != null) { jodited = true; + editor.e.on("blur", () => { + const clean = DOMPurify.sanitize(editor.editor.innerHTML); + editor.editor.innerHTML = clean; + }); } } diff --git a/DigitalLearningSolutions.Web/package.json b/DigitalLearningSolutions.Web/package.json index 782ce20b2f..46e2a66a0c 100644 --- a/DigitalLearningSolutions.Web/package.json +++ b/DigitalLearningSolutions.Web/package.json @@ -25,6 +25,7 @@ "chartist": "^0.11.4", "core-js": "^3.22.5", "date-fns": "^2.28.0", + "dompurify": "^2.3.10", "input-range-scss": "^1.5.2", "jodit": "^3.18.5", "js-cookie": "^3.0.1", @@ -37,6 +38,7 @@ "@babel/core": "^7.18.0", "@babel/preset-env": "^7.18.0", "@babel/preset-typescript": "^7.17.12", + "@types/dompurify": "^2.3.3", "@types/jest": "^27.5.1", "@types/js-cookie": "^3.0.2", "@types/js-search": "^1.4.0", From 33e3709462060c8011b8fa9fd7726b31ca29e41e Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 26 Aug 2022 14:28:47 +0100 Subject: [PATCH 2/3] Update yarn.lock --- DigitalLearningSolutions.Web/yarn.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/DigitalLearningSolutions.Web/yarn.lock b/DigitalLearningSolutions.Web/yarn.lock index d5df3f5c16..10b4d04444 100644 --- a/DigitalLearningSolutions.Web/yarn.lock +++ b/DigitalLearningSolutions.Web/yarn.lock @@ -1434,6 +1434,13 @@ resolved "https://registry.yarnpkg.com/@types/chartist/-/chartist-0.11.1.tgz#3825e6cee87f5f548e8631b2c25e3a7b597b2522" integrity sha512-85eNd7rF+e5sLnpprgcDdeqARgNvczEXaBfnrkw0292TBCE4KF/2HmOPA6dIblyHUWV4OZ2kuQBH2R12F+VwYg== +"@types/dompurify@^2.3.3": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.3.4.tgz#94e997e30338ea24d4c8d08beca91ce4dd17a1b4" + integrity sha512-EXzDatIb5EspL2eb/xPGmaC8pePcTHrkDCONjeisusLFrVfl38Pjea/R0YJGu3k9ZQadSvMqW0WXPI2hEo2Ajg== + dependencies: + "@types/trusted-types" "*" + "@types/eslint-scope@^3.7.3": version "3.7.3" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" @@ -1553,6 +1560,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/trusted-types@*": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" + integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" From 1975c28f022b083ba49ffb929c0daf897aa8ab69 Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 26 Aug 2022 14:35:07 +0100 Subject: [PATCH 3/3] Update htmleditor.ts --- .../Scripts/frameworks/htmleditor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts index 3289462a44..8577474744 100644 --- a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts @@ -3,7 +3,7 @@ import DOMPurify from 'dompurify'; let jodited = false; if (jodited === false) { - const editor = Jodit.make(".html-editor", { + const editor = Jodit.make('.html-editor', { buttons: [ 'source', '|', 'bold', @@ -58,7 +58,7 @@ if (jodited === false) { 'ul', 'ol', '|', 'undo', 'redo', - ], + ], style: { backgroundColor: '#FFF', }, @@ -66,7 +66,7 @@ if (jodited === false) { if (editor != null) { jodited = true; - editor.e.on("blur", () => { + editor.e.on('blur', () => { const clean = DOMPurify.sanitize(editor.editor.innerHTML); editor.editor.innerHTML = clean; });