diff --git a/README.md b/README.md
index b57f31d..01f9205 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ marked:
autolink: true
mangle: true
sanitizeUrl: false
+ dompurify: false,
headerIds: true
lazyload: false
prependRoot: false
@@ -61,6 +62,7 @@ marked:
- **mangle** - Escape autolinked email address with HTML character references.
* This is to obscure email address from _basic_ crawler used by spam bot, while still readable to web browsers.
- **sanitizeUrl** - Remove URLs that start with `javascript:`, `vbscript:` and `data:`.
+- **dompurify** - Enable [DOMPurify](https://github.com/cure53/DOMPurify) to be run on the rendered Markdown. See below for configuration
- **headerIds** - Insert header id, e.g. `
text
`. Useful for inserting anchor link to each paragraph with a heading.
- **anchorAlias** - Enables custom header id
* Example: `## [foo](#bar)`, id will be set as "bar".
@@ -91,6 +93,26 @@ For more options, see [Marked](https://marked.js.org/using_advanced#options). Du
## Extras
+### Sanitize HTML with DOMPurify
+
+[DOMPurify](https://github.com/cure53/DOMPurify) can be enabled to sanitize the rendered HTML.
+
+To enable it, pass an object containing the DOMPurify options:
+
+```json
+dompurify: true
+```
+
+Or you can enable specific DOMPurify options (but according to DOMPurify authors, the default options are safe):
+
+```yml
+dompurify:
+ FORBID_TAGS:
+ - "style"
+```
+
+See https://github.com/cure53/DOMPurify#can-i-configure-dompurify for a full reference of available options
+
### Definition/Description Lists
`hexo-renderer-marked` also implements description/definition lists using the same syntax as [PHP Markdown Extra][PHP Markdown Extra].
diff --git a/index.js b/index.js
index 43b1202..3781d18 100644
--- a/index.js
+++ b/index.js
@@ -14,6 +14,7 @@ hexo.config.marked = Object.assign({
autolink: true,
mangle: true,
sanitizeUrl: false,
+ dompurify: false,
headerIds: true,
anchorAlias: false,
lazyload: false,
diff --git a/lib/renderer.js b/lib/renderer.js
index 865de36..e31b91a 100644
--- a/lib/renderer.js
+++ b/lib/renderer.js
@@ -1,6 +1,10 @@
'use strict';
const marked = require('marked');
+
+let JSDOM,
+ createDOMPurify;
+
const { escape } = require('marked/src/helpers');
const { encodeURL, slugize, stripHTML, url_for, isExternalLink } = require('hexo-util');
const MarkedRenderer = marked.Renderer;
@@ -236,7 +240,7 @@ class Tokenizer extends MarkedTokenizer {
module.exports = function(data, options) {
const { post_asset_folder, marked: markedCfg, source_dir } = this.config;
- const { prependRoot, postAsset } = markedCfg;
+ const { prependRoot, postAsset, dompurify } = markedCfg;
const { path, text } = data;
// exec filter to extend renderer.
@@ -258,8 +262,23 @@ module.exports = function(data, options) {
}
}
- return marked(text, Object.assign({
+ let sanitizer = function(html) { return html; };
+
+ if (dompurify) {
+ if (createDOMPurify === undefined && JSDOM === undefined) {
+ createDOMPurify = require('dompurify');
+ JSDOM = require('jsdom').JSDOM;
+ }
+ const window = new JSDOM('').window;
+ const DOMPurify = createDOMPurify(window);
+ let param = {};
+ if (dompurify !== true) {
+ param = dompurify;
+ }
+ sanitizer = function(html) { return DOMPurify.sanitize(html, param); };
+ }
+ return sanitizer(marked(text, Object.assign({
renderer,
tokenizer
- }, markedCfg, options, { postPath }));
+ }, markedCfg, options, { postPath })));
};
diff --git a/package.json b/package.json
index 78e5871..2357660 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,10 @@
],
"license": "MIT",
"dependencies": {
- "hexo-util": "^2.1.0",
- "marked": "^2.0.0"
+ "hexo-util": "^2.5.0",
+ "marked": "^2.1.3",
+ "dompurify": "^2.3.0",
+ "jsdom": "^16.7.0"
},
"devDependencies": {
"chai": "^4.2.0",
diff --git a/test/index.js b/test/index.js
index 123fc21..1205b0a 100644
--- a/test/index.js
+++ b/test/index.js
@@ -949,4 +949,37 @@ describe('Marked renderer', () => {
result.content.should.eql('foo {% lorem %}
\n');
});
});
+
+ describe('sanitize HTML with DOMPurify', () => {
+ const body = [
+ '**safe markdown**',
+ '',
+ 'unsafe link',
+ '',
+ '[Hexo](http://hexo.io)'
+ ].join('\n');
+
+ it('sanitize enabled, default options', () => {
+ hexo.config.marked.dompurify = true;
+ const result = r({text: body});
+
+ result.should.eql([
+ 'safe markdown
\n',
+ 'unsafe link
\n',
+ 'Hexo
\n'
+ ].join(''));
+ });
+
+ it('sanitize enabled, with options', () => {
+ hexo.config.marked.dompurify = { FORBID_TAGS: ['strong'] };
+ const result = r({text: body});
+
+ result.should.eql([
+ 'safe markdown
\n',
+ 'unsafe link
\n',
+ 'Hexo
\n'
+ ].join(''));
+ });
+
+ });
});