Skip to content

Instantly share code, notes, and snippets.

@apple502j
Created October 13, 2020 12:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apple502j/b1a4af80050175d0a23021a38b28ba57 to your computer and use it in GitHub Desktop.
Save apple502j/b1a4af80050175d0a23021a38b28ba57 to your computer and use it in GitHub Desktop.
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