diff --git a/frontend/src/utils/htmlFromMarkdown.js b/frontend/src/utils/htmlFromMarkdown.js
index b2a29bf65a..06e2464266 100644
--- a/frontend/src/utils/htmlFromMarkdown.js
+++ b/frontend/src/utils/htmlFromMarkdown.js
@@ -1,9 +1,59 @@
import { marked } from 'marked';
import DOMPurify from 'dompurify';
+const VIDEO_TAG_REGEXP = new RegExp(/^::youtube\[(.*)\]$/);
+
+const parseMarkdown = (markdownText) => {
+ marked.use({
+ gfm: false,
+ extensions: [
+ {
+ name: 'videoExt',
+ level: 'inline',
+ start: (src) => {
+ const m = src.match(/^::youtube/);
+
+ if (m) {
+ return m.index;
+ }
+ },
+ tokenizer: function(src) {
+ const match = VIDEO_TAG_REGEXP.exec(src);
+
+ if (match) {
+ return {
+ type: 'videoExt',
+ raw: match[0],
+ text: match[1].trim(),
+ tokens: []
+ };
+ }
+ },
+ renderer: function(token) {
+ const videoId = token.text;
+
+ return `
+
+ `;
+ }
+ }
+ ]
+ });
+ return marked.parse(markdownText);
+}
+
/* per https://stackoverflow.com/a/34688574/272018 */
export const htmlFromMarkdown = (markdownText) => {
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
+
// set all elements owning target to target=_blank
if ('target' in node) {
node.setAttribute('target', '_blank');
@@ -16,7 +66,23 @@ export const htmlFromMarkdown = (markdownText) => {
node.setAttribute('xlink:show', 'new');
}
});
- return { __html: DOMPurify.sanitize(marked.parse(markdownText)) };
+
+ DOMPurify.addHook("uponSanitizeElement", (node, data) => {
+ if (data.tagName === "iframe") {
+ const src = node.getAttribute("src") || "";
+ // allow only youtube urls to be embedded in iframes
+ if (!src.startsWith("https://www.youtube.com/embed/")) {
+ return node.parentNode?.removeChild(node);
+ }
+ }
+ });
+
+ const config = {
+ ADD_TAGS: ['iframe'],
+ ADD_ATTR: ["allow", "allowfullscreen", "frameborder"]
+ };
+
+ return { __html: DOMPurify.sanitize(parseMarkdown(markdownText), config) };
};
export const formatUserNamesToLink = (text) => {
diff --git a/frontend/src/utils/tests/htmlFromMarkdown.test.js b/frontend/src/utils/tests/htmlFromMarkdown.test.js
index e265e2912e..bad55434f6 100644
--- a/frontend/src/utils/tests/htmlFromMarkdown.test.js
+++ b/frontend/src/utils/tests/htmlFromMarkdown.test.js
@@ -9,6 +9,29 @@ test('htmlFromMarkdown returns correct content', () => {
);
});
+test('htmlFromMarkdown with youtube tag', () => {
+ const html = htmlFromMarkdown('::youtube[UzT0i5XhsOQ]').__html;
+ expect(html).toContain('