-
-
Save apple502j/b1a4af80050175d0a23021a38b28ba57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From dcc3b8bd40d52ab6ebd453dcfc85e9a6513e036e Mon Sep 17 00:00:00 2001 | |
From: apple502j <33279053+apple502j@users.noreply.github.com> | |
Date: Tue, 13 Oct 2020 21:05:10 +0900 | |
Subject: [PATCH] use dompurify | |
--- | |
package.json | 1 + | |
src/fixup-svg-string.js | 23 ++++++++++++++++++++--- | |
test/fixup-svg-string.js | 40 ++++++++++++++++++++++------------------ | |
3 files changed, 43 insertions(+), 21 deletions(-) | |
diff --git a/package.json b/package.json | |
index bc18d4c..9d8e190 100644 | |
--- a/package.json | |
+++ b/package.json | |
@@ -22,6 +22,7 @@ | |
"dependencies": { | |
"base64-js": "1.2.1", | |
"base64-loader": "1.0.0", | |
+ "dompurify": "^2.1.1", | |
"minilog": "3.1.0", | |
"transformation-matrix": "1.15.0", | |
"scratch-render-fonts": "1.0.0-prerelease.20200507182347" | |
diff --git a/src/fixup-svg-string.js b/src/fixup-svg-string.js | |
index 1407f4e..077917f 100644 | |
--- a/src/fixup-svg-string.js | |
+++ b/src/fixup-svg-string.js | |
@@ -1,9 +1,18 @@ | |
+const purify = require('dompurify'); | |
+ | |
/** | |
* Fixup svg string prior to parsing. | |
* @param {!string} svgString String of the svg to fix. | |
+ * @param {?object} domPurifyWindow Window object used in DOMPurify. Must be provided in Node.js environment. | |
* @returns {!string} fixed svg that should be parseable. | |
*/ | |
-module.exports = function (svgString) { | |
+module.exports = function (svgString, domPurifyWindow) { | |
+ let DOMPurify = purify; // On browser, DOMPurify is already initialized with "window" | |
+ if (typeof window === 'undefined') { | |
+ // When in Node.js environment | |
+ DOMPurify = purify(domPurifyWindow); | |
+ } | |
+ | |
// Add root svg namespace if it does not exist. | |
const svgAttrs = svgString.match(/<svg [^>]*>/); | |
if (svgAttrs && svgAttrs[0].indexOf('xmlns=') === -1) { | |
@@ -47,8 +56,16 @@ module.exports = function (svgString) { | |
// Note: [\s\S] matches everything including newlines, which .* does not | |
svgString = svgString.replace(/<metadata>[\s\S]*<\/metadata>/, '<metadata></metadata>'); | |
- // Empty script tags and javascript executing | |
- svgString = svgString.replace(/<script[\s\S]*>[\s\S]*<\/script>/, '<script></script>'); | |
+ // Sanitize SVG via DOMPurify. | |
+ svgString = DOMPurify.sanitize(svgString, { | |
+ // Enable profile for SVG | |
+ USE_PROFILES: {svg: true}, | |
+ // Disable some tags that are included in "svg" profile. | |
+ // They cannot be rendered correctly anyway. | |
+ FORBID_TAGS: ['a', 'audio', 'canvas', 'video'], | |
+ // Allow data: URL for image tags (e.g. images converted from bitmap) | |
+ ADD_DATA_URI_TAGS: ['image'] | |
+ }); | |
return svgString; | |
}; | |
diff --git a/test/fixup-svg-string.js b/test/fixup-svg-string.js | |
index 31d3d52..6e39213 100644 | |
--- a/test/fixup-svg-string.js | |
+++ b/test/fixup-svg-string.js | |
@@ -1,9 +1,13 @@ | |
const test = require('tap').test; | |
const fs = require('fs'); | |
const path = require('path'); | |
+const JSDOM = require('jsdom').JSDOM; | |
const DOMParser = require('xmldom').DOMParser; | |
const fixupSvgString = require('../src/fixup-svg-string'); | |
+// Shared window object used in fixupSvgString | |
+const domPurifyWindow = new JSDOM('').window; | |
+ | |
// The browser DOMParser throws on errors by default, replicate that here | |
// by customizing the error callback to throw (defaults to logging) | |
const domParser = new DOMParser({ | |
@@ -18,7 +22,7 @@ test('fixupSvgString should make parsing fixtures not throw', t => { | |
const filePath = path.resolve(__dirname, './fixtures/hearts.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
// Make sure undefineds aren't being written into the file | |
t.equal(fixed.indexOf('undefined'), -1); | |
@@ -32,7 +36,7 @@ test('fixupSvgString should correct namespace declarations bound to reserved nam | |
const filePath = path.resolve(__dirname, './fixtures/reserved-namespace.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
// Make sure undefineds aren't being written into the file | |
t.equal(fixed.indexOf('undefined'), -1); | |
@@ -42,25 +46,26 @@ test('fixupSvgString should correct namespace declarations bound to reserved nam | |
t.end(); | |
}); | |
-test('fixupSvgString should empty script tags', t => { | |
+test('fixupSvgString should remove script tags', t => { | |
const filePath = path.resolve(__dirname, './fixtures/script.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
- // Script tag should remain but have no contents. | |
- t.equals(fixed.indexOf('<script></script>'), 207); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
+ // Script tag should not remain. | |
+ t.equals(fixed.indexOf('<script></script>'), -1); | |
// The contents of the script tag (e.g. the alert) are no longer there. | |
t.equals(fixed.indexOf('stuff inside'), -1); | |
t.end(); | |
}); | |
-test('fixupSvgString should empty script tags in onload', t => { | |
+test('fixupSvgString should remove script tags in onload', t => { | |
const filePath = path.resolve(__dirname, './fixtures/onload-script.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
- // Script tag should remain but have no contents. | |
- t.equals(fixed.indexOf('<script></script>'), 792); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
+ // Script tag should not remain. | |
+ t.equals(fixed.indexOf('<script></script>'), -1); | |
+ t.equals(fixed.indexOf('onload'), -1); | |
t.end(); | |
}); | |
@@ -68,28 +73,27 @@ test('fixupSvgString strips contents of metadata', t => { | |
const filePath = path.resolve(__dirname, './fixtures/metadata-body.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
// Metadata tag should still exist, it'll just be empty. | |
- t.equals(fixed.indexOf('<metadata></metadata>'), 207); | |
+ t.equals(fixed.indexOf('<metadata></metadata>'), 113); | |
// The contents of the metadata tag are gone. | |
t.equals(fixed.indexOf('stuff inside'), -1); | |
t.end(); | |
}); | |
-test('fixupSvgString strips contents of metadata in onload', t => { | |
+test('fixupSvgString should not add onload attribute', t => { | |
const filePath = path.resolve(__dirname, './fixtures/metadata-onload.svg'); | |
const svgString = fs.readFileSync(filePath) | |
.toString(); | |
- const fixed = fixupSvgString(svgString); | |
- // Metadata tag should still exist, it'll just be empty. | |
- t.equals(fixed.indexOf('<metadata></metadata>'), 800); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
+ t.equals(fixed.indexOf('onload'), -1); | |
t.end(); | |
}); | |
test('fixupSvgString should correct invalid mime type', t => { | |
const filePath = path.resolve(__dirname, './fixtures/invalid-cloud.svg'); | |
const svgString = fs.readFileSync(filePath, 'utf8'); | |
- const fixed = fixupSvgString(svgString); | |
+ const fixed = fixupSvgString(svgString, domPurifyWindow); | |
// Make sure we replace an invalid mime type from Photoshop exported SVGs | |
t.notEqual(svgString.indexOf('img/png'), -1); | |
@@ -101,7 +105,7 @@ test('fixupSvgString should correct invalid mime type', t => { | |
}); | |
test('fixupSvgString shouldn\'t correct non-image tags', t => { | |
- const dontFix = fixupSvgString('<text>data:img/png is not a mime type</text>'); | |
+ const dontFix = fixupSvgString('<text>data:img/png is not a mime type</text>', domPurifyWindow); | |
t.notEqual(dontFix.indexOf('img/png'), -1); | |
t.end(); | |
-- | |
2.28.0.windows.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment