From 247a6b9d76634fadf7720e74a8e4a004b8a38e5f Mon Sep 17 00:00:00 2001
From: Julien Ramboz
Date: Fri, 3 May 2019 09:46:29 -0700
Subject: [PATCH] feat(html pipe): Allow custom elements and attributes in
markdown
Let the sanitization step accept custom elements and attributes added to the markdown
fix #253
---
src/html/sanitize.js | 48 +++++++++++++++++++++++++++++++++++-
src/utils/heading-handler.js | 2 +-
test/testEmbedHandler.js | 4 +--
test/testHTML.js | 2 +-
test/testHTMLFromMarkdown.js | 32 +++++++++++++++++-------
5 files changed, 74 insertions(+), 14 deletions(-)
diff --git a/src/html/sanitize.js b/src/html/sanitize.js
index baac49e74..b32c9d60c 100644
--- a/src/html/sanitize.js
+++ b/src/html/sanitize.js
@@ -13,15 +13,61 @@ const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const helixSanitizationConfig = {
- ADD_TAGS: ['esi:include', 'esi:remove'],
+ // Allowing all ESI tags, see: https://www.w3.org/TR/esi-lang
+ ADD_TAGS: [
+ 'esi:try',
+ 'esi:attempt',
+ 'esi:except',
+
+ 'esi:choose',
+ 'esi:when',
+ 'esi:otherwise',
+
+ 'esi:include',
+ 'esi:inline',
+ 'esi:remove',
+
+ 'esi:vars',
+ 'esi:comment',
+ ],
RETURN_DOM: true,
};
+const CUSTOM_NAME_REGEX = /^\w+-\w+$/;
+
+/**
+ * Allow custom elements to be retained by the sanitization.
+ *
+ * @param {Object} DOMPurify the DOMPurify instance
+ */
+function allowCustomElements(DOMPurify) {
+ DOMPurify.addHook('uponSanitizeElement', (node, data) => {
+ if (node.nodeName && node.nodeName.match(CUSTOM_NAME_REGEX)) {
+ data.allowedTags[data.tagName] = true; // eslint-disable-line no-param-reassign
+ }
+ });
+}
+
+/**
+ * Allow custom attributes to be retained by the sanitization.
+ *
+ * @param {Object} DOMPurify the DOMPurify instance
+ */
+function allowCustomAttributes(DOMPurify) {
+ DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
+ if (data.attrName && data.attrName.match(CUSTOM_NAME_REGEX)) {
+ data.allowedAttributes[data.attrName] = true; // eslint-disable-line no-param-reassign
+ }
+ });
+}
+
function sanitize({ content }, { logger }) {
logger.log('debug', 'Sanitizing content body to avoid XSS injections.');
const globalContext = (new JSDOM('')).window;
const DOMPurify = createDOMPurify(globalContext);
+ allowCustomElements(DOMPurify);
+ allowCustomAttributes(DOMPurify);
const sanitizedBody = DOMPurify.sanitize(content.document.body, helixSanitizationConfig);
return {
content: {
diff --git a/src/utils/heading-handler.js b/src/utils/heading-handler.js
index 5c3b5b91b..fb0f30bf7 100644
--- a/src/utils/heading-handler.js
+++ b/src/utils/heading-handler.js
@@ -45,7 +45,7 @@ class HeadingHandler {
// Inject the id after transformation
const n = Object.assign({}, node);
const el = fallback(h, n);
- el.properties.id = el.properties.id || `user-content-${headingIdentifier}`;
+ el.properties.id = el.properties.id || headingIdentifier;
return el;
};
}
diff --git a/test/testEmbedHandler.js b/test/testEmbedHandler.js
index 6f1c413f7..93bde3833 100644
--- a/test/testEmbedHandler.js
+++ b/test/testEmbedHandler.js
@@ -129,7 +129,7 @@ https://www.youtube.com/watch?v=KOxbO0EI4MA
Here comes an embed.
-
+
`).window.document.body,
);
});
@@ -167,7 +167,7 @@ Here comes an embed.
Here comes an embed.
-
+
`).window.document.body,
);
});
diff --git a/test/testHTML.js b/test/testHTML.js
index 8caa8bade..7f53fa51a 100644
--- a/test/testHTML.js
+++ b/test/testHTML.js
@@ -719,8 +719,8 @@ ${context.content.document.body.innerHTML}`,
);
assert.equal(200, result.response.status);
assertEquivalentNode(
- new JSDOM('Foo
Bar
Baz').window.document.body,
new JSDOM(result.response.body).window.document.body,
+ new JSDOM('Foo
Bar
Baz').window.document.body,
);
});
});
diff --git a/test/testHTMLFromMarkdown.js b/test/testHTMLFromMarkdown.js
index d1f839b00..529bfd4b6 100644
--- a/test/testHTMLFromMarkdown.js
+++ b/test/testHTMLFromMarkdown.js
@@ -143,7 +143,7 @@ describe('Testing Markdown conversion', () => {
Hello World
`, `
- Hello
+ Hello
Hello World\n
`);
await assertMd(
@@ -166,10 +166,10 @@ describe('Testing Markdown conversion', () => {
>
> bar
`, `
- Foo
+ Foo
bar
- Foo
+ Foo
bar
`);
@@ -190,7 +190,7 @@ describe('Testing Markdown conversion', () => {
Hello World [link](Foo
+ Foo
Hello World [link](<foobar)
`);
});
@@ -201,7 +201,7 @@ describe('Testing Markdown conversion', () => {
Hello World [link](foo bar)
`, `
- Foo
+ Foo
Hello World [link](foo bar)
`);
});
@@ -212,7 +212,7 @@ describe('Testing Markdown conversion', () => {
Hello World [link](λ)
`, `
- Foo
+ Foo
Hello World link
`);
});
@@ -225,7 +225,7 @@ describe('Testing Markdown conversion', () => {
`, `
- Foo
+ Foo
`);
});
@@ -236,7 +236,7 @@ describe('Testing Markdown conversion', () => {
Hello World [link](λ)
`, `
- Foo Bar
+ Foo Bar
Hello World link
`);
});
@@ -296,8 +296,22 @@ describe('Testing Markdown conversion', () => {
# location
Foo
`, `
- location
+ location
Foo
`);
});
+
+ it('Accept custom elements and attributes', async () => {
+ await assertMd(`
+ # Foo
+ Bar
+ Waldo
+ `, `
+ Foo
+ Bar
+
+ Waldo
+
+ `);
+ });
});