Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"vite": "^5.1.6"
},
"dependencies": {
"dompurify": "^3.2.3",
"he": "^1.2.0",
"jszip": "^3.10.1",
"vite-plugin-dts": "^4.0.2"
Expand Down
3 changes: 2 additions & 1 deletion src/imscc/resource/discussion.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page } from "../types";
import DOMPurify from "dompurify";

const safeTags = (str: string) =>
str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
Expand All @@ -8,7 +9,7 @@ export const discussionDocument = (page: Page, _id: string) => ({
<topic xmlns="http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imsdt_v1p1.xsd">
<title>${page.title}</title>
<text texttype="text/html">${safeTags(
typeof page.content === "string" ? page.content : ""
typeof page.content === "string" ? DOMPurify.sanitize(page.content) : ""
)}</text>
</topic>`,
ext: "xml",
Expand Down
7 changes: 6 additions & 1 deletion src/imscc/resource/html.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FILEBASE_PLACEHOLDER } from "../constants";
import { Config, Page } from "../types";
import DOMPurify from "dompurify";

export const cssFromConfig = (config?: Config) => {
if (!config) {
Expand Down Expand Up @@ -29,7 +30,11 @@ const templateHtml = (page: Page, id: string, options?: Config) => {
<meta name="editing_roles" content="teachers"/>${styleTag}
</head>
<body>
${page.content}
${
typeof page.content === "string"
? DOMPurify.sanitize(page.content)
: page.content
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would it be that it's not a string?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above it defaults to ""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

content: string | Section[];

when it's an assessment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do those sections also have HTML?

Copy link
Copy Markdown
Contributor

@dcollien dcollien Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g.

const sanitizeAnswer = <T extends Answer>(answer: T) => ({
  ...answer,
  text: sanitize(answer.text)
});

const sanitizeAnswerArray = <T extends Answer>(answers: T[] | undefined) => answers ? answers.map(sanitizeAnswer) : undefined;

const sanitizeMatches = (matches: Match[] | undefined) => matches ?
  matches.map((match) => ({
    pair: [sanitizeAnswer(match.pair[0]), sanitizeAnswer(match.pair[1])]
  })): undefined;

const sanitize = (content: string | Section[]) => {
  if (typeof content === "string") {
    return DOMPurify.sanitize(content);
  } else {
    const sanitizedSection = content.map((section) => ({
      ...section,
      title: sanitize(section.title),
      question: sanitize(section.question),
      answers: sanitizeAnswerArray(section.answers),
      choices: sanitizeAnswerArray(section.choices),
      matches: sanitizeMatches(section.matches)
    }));
    return sanitizedSection;
  }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: any user editable field that could possibly be reflected back to a user as HTML needs to be sanitized

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do those sections also have HTML?

yes, only questions that can be in html format. currently i sanitized the question in coursemagic side because it's in escaped format, i sanitze it before escaping it. should the imscc-packger be the one that responsible for sanitizing the html?

}
</body>
</html>`;
};
Expand Down