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;
};
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 = {
// $FlowFixMe - get/set properties not yet supported
get SUPPORT_RANGE_BOUNDS() {
@ -32,6 +47,12 @@ const FEATURES = {
const value = testRangeBounds(document);
Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {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;
type ImageCache = {[string]: Promise<?ImageElement>};
import FEATURES from './Feature';
export default class ImageLoader {
origin: string;
options: Options;
@ -30,12 +32,22 @@ export default class ImageLoader {
return src;
}
if (this.options.allowTaint === true || this.isInlineImage(src) || this.isSameOrigin(src)) {
if (isSVG(src)) {
if (this.options.allowTaint === true || FEATURES.SUPPORT_SVG_DRAWING) {
return this.addImage(src, src);
}
} 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
}
}
}
loadCanvas(node: HTMLCanvasElement): string {
const key = String(this._index++);
@ -43,10 +55,6 @@ export default class ImageLoader {
return key;
}
isInlineImage(src: string): boolean {
return /data:image\/.*;base64,/i.test(src);
}
hasImageInCache(key: string): boolean {
return typeof this.cache[key] !== 'undefined';
}
@ -112,3 +120,11 @@ export class ImageStore {
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 querystring from 'querystring';
const DOWNLOAD_REFTESTS = true;
const DOWNLOAD_REFTESTS = false;
const query = querystring.parse(location.search.replace(/^\?/, ''));
const downloadResult = (filename, data) => {
@ -126,7 +126,7 @@ const assertPath = (result, expected, desc) => {
const delta = 10;
if (REFTEST) {
if (REFTEST && query.refTest === 'true') {
const RESULTS = parseRefTest(result);
REFTEST.forEach(({action, line, ...args}, i) => {
const RESULT = RESULTS[i];