Skip to content

Commit

Permalink
fix(parseURL, hasProtocol, isScriptProtocol): ignore leading whitespa…
Browse files Browse the repository at this point in the history
…ces (#170)

Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
danielroe and pi0 authored Aug 24, 2023
1 parent e600fc0 commit c9e385e
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 15 deletions.
25 changes: 13 additions & 12 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ export interface ParsedHost {
* @returns A parsed URL object.
*/
export function parseURL(input = "", defaultProto?: string): ParsedURL {
const dataMatch = input.match(/^(data:|blob:)/);
if (dataMatch) {
const proto = dataMatch[1];
const _specialProtoMatch = input.match(
/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/
);
if (_specialProtoMatch) {
const [, _proto, _pathname = ""] = _specialProtoMatch;
return {
protocol: proto,
pathname: input.slice(proto.length),
href: input,
protocol: _proto,
pathname: _pathname,
href: _proto + _pathname,
auth: "",
host: "",
search: "",
Expand All @@ -46,12 +48,11 @@ export function parseURL(input = "", defaultProto?: string): ParsedURL {
return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
}

const [protocol = "", auth, hostAndPath = ""] = (
input.replace(/\\/g, "/").match(/([^/:]+:)?\/\/([^/@]+@)?(.*)/) || []
).splice(1);
const [host = "", path = ""] = (
hostAndPath.match(/([^#/?]*)(.*)?/) || []
).splice(1);
const [, protocol = "", auth, hostAndPath = ""] =
input
.replace(/\\/g, "/")
.match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/) || [];
const [, host = "", path = ""] = hostAndPath.match(/([^#/?]*)(.*)?/) || [];
const { pathname, search, hash } = parsePath(
path.replace(/\/(?=[A-Za-z]:)/, "")
);
Expand Down
6 changes: 3 additions & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function isRelative(inputString: string) {
return ["./", "../"].some((string_) => inputString.startsWith(string_));
}

const PROTOCOL_STRICT_REGEX = /^\w{2,}:([/\\]{1,2})/;
const PROTOCOL_REGEX = /^\w{2,}:([/\\]{2})?/;
const PROTOCOL_STRICT_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{1,2})/;
const PROTOCOL_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{2})?/;
const PROTOCOL_RELATIVE_REGEX = /^([/\\]\s*){2,}[^/\\]/;

export interface HasProtocolOptions {
Expand Down Expand Up @@ -43,7 +43,7 @@ export function hasProtocol(
);
}

const PROTOCOL_SCRIPT_RE = /^(blob|data|javascript|vbscript):$/;
const PROTOCOL_SCRIPT_RE = /^[\s\0]*(blob|data|javascript|vbscript):$/;

export function isScriptProtocol(protocol?: string) {
return !!protocol && PROTOCOL_SCRIPT_RE.test(protocol);
Expand Down
1 change: 1 addition & 0 deletions test/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe("normalizeURL", () => {
"http://localhost/abc/deg%2f%3f%26?email=some+v1@email.com&foo=bar":
"http://localhost/abc/deg%2F%3F%26?email=some+v1@email.com&foo=bar",
"http://example.com/foo\\bar": "http://example.com/foo/bar",
"\0http://google.com": "http://google.com",
};

const validURLS = [
Expand Down
35 changes: 35 additions & 0 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ describe("parseURL", () => {
search: "?url=http://0.0.0.0/2.svg",
},
},
{
input: "javascript:alert('hello')",
out: {
protocol: "javascript:",
auth: "",
host: "",
href: "javascript:alert('hello')",
pathname: "alert('hello')",
search: "",
hash: "",
},
},
{
input: "https://domain.test:3000#owo",
out: {
Expand Down Expand Up @@ -105,6 +117,29 @@ describe("parseURL", () => {
hash: "",
},
},
{
input: "\0https://invalid.com",
out: {
protocol: "https:",
auth: "",
host: "invalid.com",
pathname: "",
search: "",
hash: "",
},
},
{
input: "\0javascript:alert('hello')",
out: {
protocol: "javascript:",
auth: "",
host: "",
href: "javascript:alert('hello')",
pathname: "alert('hello')",
search: "",
hash: "",
},
},
];

for (const t of tests) {
Expand Down
20 changes: 20 additions & 0 deletions test/utilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
withHttps,
withoutProtocol,
withProtocol,
isScriptProtocol,
} from "../src";

describe("hasProtocol", () => {
Expand All @@ -28,6 +29,10 @@ describe("hasProtocol", () => {

// Has protocol (non strict)
{ input: "tel:", out: [true, false, true] },
{ input: "javascript:alert(true)", out: [true, false, true] },
{ input: " javascript:alert(true)", out: [true, false, true] },
{ input: "\0javascript:alert(true)", out: [true, false, true] },
{ input: "\0https://", out: [true, true, true] },
{ input: "tel:123456", out: [true, false, true] },
{ input: "mailto:support@example.com", out: [true, false, true] },

Expand All @@ -52,6 +57,21 @@ describe("hasProtocol", () => {
}
});

describe("isScriptProtocol", () => {
const tests = [
{ input: "blob:", out: true },
{ input: "data:", out: true },
{ input: "javascript:", out: true },
{ input: "vbscript:", out: true },
{ input: "\0vbscript:", out: true },
];
for (const t of tests) {
test(t.input.toString(), () => {
expect(isScriptProtocol(t.input)).toBe(t.out);
});
}
});

describe("isRelative", () => {
const tests = [
{ input: "/", out: false },
Expand Down

0 comments on commit c9e385e

Please sign in to comment.