feat: added html sanitizer for remote rendering (#1128)

Co-authored-by: Joe Pea <joe@trusktr.io>
This commit is contained in:
Anix 2020-06-17 23:55:59 +05:30 committed by GitHub
parent 0bf03f5810
commit 714ef29afe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 2005 additions and 1002 deletions

View File

@ -19,6 +19,7 @@ module.exports = {
rules: {
'prettier/prettier': ['error'],
camelcase: ['warn'],
'no-useless-escape': ['warn'],
curly: ['error', 'all'],
'dot-notation': ['error'],
eqeqeq: ['error'],

2937
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -57,6 +57,7 @@
"*.js": "eslint --fix"
},
"dependencies": {
"dompurify": "^2.0.8",
"marked": "^0.7.0",
"medium-zoom": "^1.0.5",
"opencollective-postinstall": "^2.0.2",
@ -82,7 +83,7 @@
"esm": "^3.1.4",
"husky": "^3.1.0",
"jsdom": "^16.2.2",
"lerna": "^3.17.0",
"lerna": "^3.22.1",
"lint-staged": "^10.1.2",
"live-server": "^1.2.1",
"mkdirp": "^0.5.1",

View File

@ -3,6 +3,7 @@ import { resolve, basename } from 'path';
import resolvePathname from 'resolve-pathname';
import fetch from 'node-fetch';
import debug from 'debug';
import DOMPurify from 'dompurify';
import { AbstractHistory } from '../../src/core/router/history/abstract';
import { Compiler } from '../../src/core/render/compiler';
import { isAbsolutePath } from '../../src/core/router/util';
@ -13,6 +14,32 @@ function cwd(...args) {
return resolve(process.cwd(), ...args);
}
function isExternal(url) {
let match = url.match(
/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
);
if (
typeof match[1] === 'string' &&
match[1].length > 0 &&
match[1].toLowerCase() !== location.protocol
) {
return true;
}
if (
typeof match[2] === 'string' &&
match[2].length > 0 &&
match[2].replace(
new RegExp(
':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
),
''
) !== location.host
) {
return true;
}
return false;
}
function mainTpl(config) {
let html = `<nav class="app-nav${
config.repo ? '' : ' no-badge'
@ -60,6 +87,7 @@ export default class Renderer {
async renderToString(url) {
this.url = url = this.router.parse(url).path;
this.isRemoteUrl = isExternal(this.url);
const { loadSidebar, loadNavbar, coverpage } = this.config;
const mainFile = this._getPath(url);
@ -95,9 +123,8 @@ export default class Renderer {
this._renderHtml('cover', await this._render(coverFile), 'cover');
}
const html = this.html;
const html = this.isRemoteUrl ? DOMPurify.sanitize(this.html) : this.html;
this.html = this.template;
return html;
}

View File

@ -43,6 +43,11 @@
"tweezer.js": "^1.4.0"
}
},
"dompurify": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.8.tgz",
"integrity": "sha512-vIOSyOXkMx81ghEalh4MLBtDHMx1bhKlaqHDMqM2yeitJ996SLOk5mGdDpI9ifJAgokred8Rmu219fX4OltqXw=="
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",

View File

@ -17,6 +17,7 @@
"dependencies": {
"debug": "^4.1.1",
"docsify": "^4.11.2",
"dompurify": "^2.0.8",
"node-fetch": "^2.6.0",
"resolve-pathname": "^3.0.0"
}

View File

@ -20,6 +20,32 @@ function loadNested(path, qs, file, next, vm, first) {
).then(next, _ => loadNested(path, qs, file, next, vm));
}
function isExternal(url) {
let match = url.match(
/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
);
if (
typeof match[1] === 'string' &&
match[1].length > 0 &&
match[1].toLowerCase() !== location.protocol
) {
return true;
}
if (
typeof match[2] === 'string' &&
match[2].length > 0 &&
match[2].replace(
new RegExp(
':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
),
''
) !== location.host
) {
return true;
}
return false;
}
export function fetchMixin(proto) {
let last;
@ -84,6 +110,7 @@ export function fetchMixin(proto) {
const file = this.router.getFile(path);
const req = request(file + qs, true, requestHeaders);
this.isRemoteUrl = isExternal(file);
// Current page is html
this.isHTML = /\.html$/g.test(file);

View File

@ -1,5 +1,6 @@
/* eslint-disable no-unused-vars */
import tinydate from 'tinydate';
import DOMPurify from 'dompurify';
import * as dom from '../util/dom';
import cssVars from '../util/polyfill/css-vars';
import { callHook } from '../init/lifecycle';
@ -172,6 +173,7 @@ export function renderMixin(proto) {
},
tokens => {
html = this.compiler.compile(tokens);
html = this.isRemoteUrl ? DOMPurify.sanitize(html) : html;
callback();
next();
}