Skip to content

Commit

Permalink
Allow namespace in SVG, only check top level element
Browse files Browse the repository at this point in the history
  • Loading branch information
rlidwka committed Jun 3, 2021
1 parent 7e1b4f8 commit 8dc8b85
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 6 deletions.
17 changes: 14 additions & 3 deletions lib/parse_stream/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ var STATE_IGNORE = 2; // we got all the data we want, skip the rest
// max size for pre-svg-tag comments plus svg tag itself
var MAX_DATA_LENGTH = 65536;

var SVG_HEADER_RE = /<svg\s[^>]+>/;
// used to get opening tag for top level element in XML,
// skipping comments (<!--), directives (<?) and cdata (<![)
var SVG_HEADER_RE = /<[-_.:a-zA-Z0-9][^>]*>/;

// test if the top level element is svg + optional namespace,
// used to skip svg embedded in html
var SVG_TAG_RE = /^<([-_.:a-zA-Z0-9]+:)?svg\s/;

var SVG_WIDTH_RE = /[^-]\bwidth="([^%]+?)"|[^-]\bwidth='([^%]+?)'/;
var SVG_HEIGHT_RE = /\bheight="([^%]+?)"|\bheight='([^%]+?)'/;
var SVG_VIEWBOX_RE = /\bview[bB]ox="(.+?)"|\bview[bB]ox='(.+?)'/;
Expand Down Expand Up @@ -48,9 +55,13 @@ function units(str) {


function parseSvg(str) {
if (!SVG_HEADER_RE.test(str)) return;
// get top level element
var svgTag = (str.match(SVG_HEADER_RE) || [ '' ])[0];

// test if top level element is <svg>
if (!SVG_TAG_RE.test(svgTag)) return;

var attrs = svgAttrs(str.match(SVG_HEADER_RE)[0]);
var attrs = svgAttrs(svgTag);
var width = parseFloat(attrs.width);
var height = parseFloat(attrs.height);

Expand Down
17 changes: 14 additions & 3 deletions lib/parse_sync/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ function canBeSvg(buf) {
}


var SVG_HEADER_RE = /<svg\s[^>]+>/;
// used to get opening tag for top level element in XML,
// skipping comments (<!--), directives (<?) and cdata (<![)
var SVG_HEADER_RE = /<[-_.:a-zA-Z0-9][^>]*>/;

// test if the top level element is svg + optional namespace,
// used to skip svg embedded in html
var SVG_TAG_RE = /^<([-_.:a-zA-Z0-9]+:)?svg\s/;

var SVG_WIDTH_RE = /[^-]\bwidth="([^%]+?)"|[^-]\bwidth='([^%]+?)'/;
var SVG_HEIGHT_RE = /\bheight="([^%]+?)"|\bheight='([^%]+?)'/;
var SVG_VIEWBOX_RE = /\bview[bB]ox="(.+?)"|\bview[bB]ox='(.+?)'/;
Expand Down Expand Up @@ -61,9 +68,13 @@ module.exports = function (data) {
str += String.fromCharCode(data[i]);
}

if (!SVG_HEADER_RE.test(str)) return;
// get top level element
var svgTag = (str.match(SVG_HEADER_RE) || [ '' ])[0];

// test if top level element is <svg>
if (!SVG_TAG_RE.test(svgTag)) return;

var attrs = svgAttrs(str.match(SVG_HEADER_RE)[0]);
var attrs = svgAttrs(svgTag);
var width = parseFloat(attrs.width);
var height = parseFloat(attrs.height);

Expand Down
64 changes: 64 additions & 0 deletions test/test_formats.js
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,33 @@ describe('File formats', function () {
assert.deepStrictEqual(size, { width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' });
});

it('should not parse HTML as SVG', async function () {
let buf = Buffer.from('<html><body><svg width="5" height="4"></svg></body></html>');

await assert.rejects(
async () => probe(Readable.from([ buf ])),
/unrecognized file format/
);
});

it('should skip initial comments and directives', async function () {
let buf = Buffer.from('<?xml version="1.0"?><!-- comment --><svg width="5" height="4"></svg>');

assert.deepStrictEqual(
await probe(Readable.from([ buf ])),
{ width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' }
);
});

it('should allow SVG namespace', async function () {
let buf = Buffer.from('<aaa:svg xmlns:aaa="http://www.w3.org/2000/svg" width="5" height="4"></aaa:svg>');

assert.deepStrictEqual(
await probe(Readable.from([ buf ])),
{ width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' }
);
});

it('should skip BOM', async function () {
let size = await probe(Readable.from([ Buffer.from('\ufeff <svg width="5" height="4"></svg>') ]));

Expand Down Expand Up @@ -1349,6 +1376,13 @@ describe('File formats', function () {
/unrecognized file format/
);
});

it('early termination', async function () {
await assert.rejects(
async () => probe(Readable.from([ Buffer.from('<svg width="5" height="5"') ])),
/unrecognized file format/
);
});
});
});

Expand Down Expand Up @@ -1385,6 +1419,30 @@ describe('File formats', function () {
assert.deepStrictEqual(size, { width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' });
});

it('should not parse HTML as SVG', async function () {
let buf = Buffer.from('<html><body><svg width="5" height="4"></svg></body></html>');

assert.strictEqual(probe.sync(buf), null);
});

it('should skip initial comments and directives', async function () {
let buf = Buffer.from('<?xml version="1.0"?><!-- comment --><svg width="5" height="4"></svg>');

assert.deepStrictEqual(
probe.sync(buf),
{ width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' }
);
});

it('should allow SVG namespace', async function () {
let buf = Buffer.from('<aaa:svg xmlns:aaa="http://www.w3.org/2000/svg" width="5" height="4"></aaa:svg>');

assert.deepStrictEqual(
probe.sync(buf),
{ width: 5, height: 4, type: 'svg', mime: 'image/svg+xml', wUnits: 'px', hUnits: 'px' }
);
});

it('should skip BOM', async function () {
let size = probe.sync(Buffer.from('\ufeff <svg width="5" height="4"></svg>'));

Expand Down Expand Up @@ -1457,6 +1515,12 @@ describe('File formats', function () {

assert.deepStrictEqual(size, null);
});

it('early termination', function () {
let size = probe.sync(Buffer.from('<svg width="5" height="5"'));

assert.deepStrictEqual(size, null);
});
});
});

Expand Down

0 comments on commit 8dc8b85

Please sign in to comment.