Skip to content

Commit

Permalink
Fix data URL should not support query strings (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker authored and sindresorhus committed Sep 20, 2019
1 parent 02913ab commit fb92bcb
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 18 deletions.
22 changes: 11 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ const testParameter = (name, filters) => {
return filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
};

const normalizeDataURL = urlString => {
const parts = urlString.trim().match(/^data:(.*?),(.*)$/);
const normalizeDataURL = (urlString, {stripHash}) => {
const parts = urlString.match(/^data:(.*?),(.*?)(?:#(.*))?$/);

if (!parts) {
throw new Error(`Invalid URL: ${urlString}`);
}

const mediaType = parts[1].split(';');
const body = parts[2];
const hash = stripHash ? '' : parts[3];

let base64 = false;

Expand All @@ -35,7 +36,7 @@ const normalizeDataURL = urlString => {
value = value.toLowerCase();
}

return `${key}=${value}`;
return `${key}${value ? `=${value}` : ''}`;
});

const normalizedMediaType = [
Expand All @@ -50,7 +51,7 @@ const normalizeDataURL = urlString => {
normalizedMediaType.unshift(mimeType);
}

return `data:${normalizedMediaType.join(';')},${base64 ? body.trim() : body}`;
return `data:${normalizedMediaType.join(';')},${base64 ? body.trim() : body}${hash ? `#${hash}` : ''}`;
};

const normalizeUrl = (urlString, options) => {
Expand Down Expand Up @@ -84,11 +85,16 @@ const normalizeUrl = (urlString, options) => {

urlString = urlString.trim();

// Data URL
if (/^data:/i.test(urlString)) {
return normalizeDataURL(urlString, options);
}

const hasRelativeProtocol = urlString.startsWith('//');
const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);

// Prepend protocol
if (!isRelativeUrl && !/^data:/i.test(urlString)) {
if (!isRelativeUrl) {
urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
}

Expand Down Expand Up @@ -177,12 +183,6 @@ const normalizeUrl = (urlString, options) => {
urlObj.searchParams.sort();
}

// Data URL
if (urlObj.protocol === 'data:') {
const url = normalizeDataURL(`${urlObj.protocol}${urlObj.pathname}`);
return `${url}${urlObj.search}${urlObj.hash}`;
}

if (options.removeTrailingSlash) {
urlObj.pathname = urlObj.pathname.replace(/\/$/, '');
}
Expand Down
29 changes: 22 additions & 7 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ test('remove duplicate pathname slashes', t => {
t.is(normalizeUrl('http://sindresorhus.com//foo'), 'http://sindresorhus.com/foo');
});

test('deprecated options', t => {
t.throws(() => normalizeUrl('', {normalizeHttps: true}), 'options.normalizeHttps is renamed to options.forceHttp');
t.throws(() => normalizeUrl('', {normalizeHttp: true}), 'options.normalizeHttp is renamed to options.forceHttps');
t.throws(() => normalizeUrl('', {stripFragment: true}), 'options.stripFragment is renamed to options.stripHash');
});

test('data URL', t => {
// Invalid URL.
t.throws(() => normalizeUrl('data:'), 'Invalid URL: data:');
Expand All @@ -221,20 +227,24 @@ test('data URL', t => {
// Lowercase the MIME type.
t.is(normalizeUrl('data:TEXT/plain,foo'), 'data:text/plain,foo');

// Strip empty hash.
t.is(normalizeUrl('data:,foo# '), 'data:,foo');

// Key only mediaType attribute.
t.is(normalizeUrl('data:text/plain;foo=,'), 'data:text/plain;foo,');
t.is(normalizeUrl('data:text/plain; foo,'), 'data:text/plain;foo,');

// Lowercase the charset.
t.is(normalizeUrl('data:text/plain;charset=UTF-8,foo'), 'data:text/plain;charset=utf-8,foo');

// Remove spaces after the comma when it's base64.
t.is(normalizeUrl('data:image/gif;base64, R0lGODlhAQABAAAAACw= ?foo=bar'), 'data:image/gif;base64,R0lGODlhAQABAAAAACw=?foo=bar');
t.is(normalizeUrl('data:image/gif;base64, R0lGODlhAQABAAAAACw= #foo #bar'), 'data:image/gif;base64,R0lGODlhAQABAAAAACw=#foo #bar');

// Keep spaces when it's not base64.
t.is(normalizeUrl('data:text/plain;charset=utf-8, foo ?foo=bar'), 'data:text/plain;charset=utf-8, foo?foo=bar');

// Data URL with query and hash.
t.is(normalizeUrl('data:image/gif;base64,R0lGODlhAQABAAAAACw=?foo=bar#baz'), 'data:image/gif;base64,R0lGODlhAQABAAAAACw=?foo=bar#baz');
t.is(normalizeUrl('data:text/plain;charset=utf-8, foo #bar'), 'data:text/plain;charset=utf-8, foo #bar');

// Options.
t.is(normalizeUrl('data:text/plain;charset=utf-8,www.foo/index.html?foo=bar&a=a&utm_medium=test#baz', {
const options = {
defaultProtocol: 'http:',
normalizeProtocol: true,
forceHttp: true,
Expand All @@ -245,5 +255,10 @@ test('data URL', t => {
sortQueryParameters: true,
removeTrailingSlash: true,
removeDirectoryIndex: true
}), 'data:text/plain;charset=utf-8,www.foo/index.html?a=a&foo=bar');
};
t.is(normalizeUrl('data:,sindresorhus.com/', options), 'data:,sindresorhus.com/');
t.is(normalizeUrl('data:,sindresorhus.com/index.html', options), 'data:,sindresorhus.com/index.html');
t.is(normalizeUrl('data:,sindresorhus.com?foo=bar&a=a&utm_medium=test', options), 'data:,sindresorhus.com?foo=bar&a=a&utm_medium=test');
t.is(normalizeUrl('data:,foo#bar', options), 'data:,foo');
t.is(normalizeUrl('data:,www.sindresorhus.com', options), 'data:,www.sindresorhus.com');
});

0 comments on commit fb92bcb

Please sign in to comment.