Don't render SVG nodes if it taints canvas

This commit is contained in:
Niklas von Hertzen 2017-08-13 13:11:03 +08:00
parent c765e2042f
commit 37a9249a4a
3 changed files with 47 additions and 10 deletions

View File

@ -25,6 +25,21 @@ const testRangeBounds = document => {
return false; return false;
}; };
const testSVG = document => {
const img = new Image();
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
img.src = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>`;
try {
ctx.drawImage(img, 0, 0);
canvas.toDataURL();
} catch (e) {
return false;
}
return true;
};
const FEATURES = { const FEATURES = {
// $FlowFixMe - get/set properties not yet supported // $FlowFixMe - get/set properties not yet supported
get SUPPORT_RANGE_BOUNDS() { get SUPPORT_RANGE_BOUNDS() {
@ -32,6 +47,12 @@ const FEATURES = {
const value = testRangeBounds(document); const value = testRangeBounds(document);
Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value}); Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value});
return value; return value;
},
get SUPPORT_SVG_DRAWING() {
'use strict';
const value = testSVG(document);
Object.defineProperty(FEATURES, 'SUPPORT_SVG_DRAWING', {value});
return value;
} }
}; };

View File

@ -7,6 +7,8 @@ import type Logger from './Logger';
export type ImageElement = Image | HTMLCanvasElement; export type ImageElement = Image | HTMLCanvasElement;
type ImageCache = {[string]: Promise<?ImageElement>}; type ImageCache = {[string]: Promise<?ImageElement>};
import FEATURES from './Feature';
export default class ImageLoader { export default class ImageLoader {
origin: string; origin: string;
options: Options; options: Options;
@ -30,10 +32,20 @@ export default class ImageLoader {
return src; return src;
} }
if (this.options.allowTaint === true || this.isInlineImage(src) || this.isSameOrigin(src)) { if (isSVG(src)) {
return this.addImage(src, src); if (this.options.allowTaint === true || FEATURES.SUPPORT_SVG_DRAWING) {
} else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) { return this.addImage(src, src);
// TODO proxy }
} else {
if (
this.options.allowTaint === true ||
isInlineImage(src) ||
this.isSameOrigin(src)
) {
return this.addImage(src, src);
} else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) {
// TODO proxy
}
} }
} }
@ -43,10 +55,6 @@ export default class ImageLoader {
return key; return key;
} }
isInlineImage(src: string): boolean {
return /data:image\/.*;base64,/i.test(src);
}
hasImageInCache(key: string): boolean { hasImageInCache(key: string): boolean {
return typeof this.cache[key] !== 'undefined'; return typeof this.cache[key] !== 'undefined';
} }
@ -112,3 +120,11 @@ export class ImageStore {
return index === -1 ? null : this._images[index]; return index === -1 ? null : this._images[index];
} }
} }
const INLINE_SVG = /^data:image\/svg\+xml/i;
const INLINE_IMG = /^data:image\/.*;base64,/i;
const isInlineImage = (src: string): boolean => INLINE_IMG.test(src);
const isSVG = (src: string): boolean =>
src.substr(-3).toLowerCase() === 'svg' || INLINE_SVG.test(src);

View File

@ -3,7 +3,7 @@ import parseRefTest from '../scripts/parse-reftest';
import reftests from './reftests'; import reftests from './reftests';
import querystring from 'querystring'; import querystring from 'querystring';
const DOWNLOAD_REFTESTS = true; const DOWNLOAD_REFTESTS = false;
const query = querystring.parse(location.search.replace(/^\?/, '')); const query = querystring.parse(location.search.replace(/^\?/, ''));
const downloadResult = (filename, data) => { const downloadResult = (filename, data) => {
@ -126,7 +126,7 @@ const assertPath = (result, expected, desc) => {
const delta = 10; const delta = 10;
if (REFTEST) { if (REFTEST && query.refTest === 'true') {
const RESULTS = parseRefTest(result); const RESULTS = parseRefTest(result);
REFTEST.forEach(({action, line, ...args}, i) => { REFTEST.forEach(({action, line, ...args}, i) => {
const RESULT = RESULTS[i]; const RESULT = RESULTS[i];