Skip to content

Commit

Permalink
Add backtrack protection to 6.x
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Sep 12, 2024
1 parent 28a5b27 commit aa2469d
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 54 deletions.
102 changes: 102 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@types/node": "^20.4.9",
"@types/semver": "^7.3.1",
"@vitest/coverage-v8": "^1.4.0",
"recheck": "^4.4.5",
"semver": "^7.3.5",
"size-limit": "^11.1.2",
"typescript": "^5.1.6"
Expand Down
61 changes: 14 additions & 47 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,7 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "+",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
],
[
Expand Down Expand Up @@ -1397,7 +1397,7 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
".",
],
Expand Down Expand Up @@ -1430,13 +1430,13 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
],
[
["/route.html", ["/route.html", "route", "html"]],
["/route", null],
["/route.html.json", ["/route.html.json", "route", "html.json"]],
["/route.html.json", ["/route.html.json", "route.html", "json"]],
],
[
[{}, null],
Expand All @@ -1459,13 +1459,13 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "?",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
],
[
["/route", ["/route", "route", undefined]],
["/route.json", ["/route.json", "route", "json"]],
["/route.json.html", ["/route.json.html", "route", "json.html"]],
["/route.json.html", ["/route.json.html", "route.json", "html"]],
],
[
[{ test: "route" }, "/route"],
Expand All @@ -1491,13 +1491,13 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "?",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
],
[
["/route", ["/route", "route", undefined]],
["/route.json", ["/route.json", "route", "json"]],
["/route.json.html", ["/route.json.html", "route", "json.html"]],
["/route.json.html", ["/route.json.html", "route.json", "html"]],
],
[
[{ test: "route" }, "/route"],
Expand Down Expand Up @@ -2084,7 +2084,7 @@ const TESTS: Test[] = [
prefix: "",
suffix: "",
modifier: "?",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\()[^\\/#\\?])+?",
},
")",
],
Expand Down Expand Up @@ -2290,7 +2290,7 @@ const TESTS: Test[] = [
prefix: ".",
suffix: "",
modifier: "",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
},
],
[
Expand Down Expand Up @@ -2356,14 +2356,14 @@ const TESTS: Test[] = [
[
{
name: "foo",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\$)[^\\/#\\?])+?",
prefix: "$",
suffix: "",
modifier: "",
},
{
name: "bar",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!\\$)[^\\/#\\?])+?",
prefix: "$",
suffix: "",
modifier: "?",
Expand Down Expand Up @@ -2392,14 +2392,14 @@ const TESTS: Test[] = [
},
{
name: "attr2",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!-)[^\\/#\\?])+?",
prefix: "-",
suffix: "",
modifier: "?",
},
{
name: "attr3",
pattern: "[^\\/#\\?]+?",
pattern: "(?:(?!-)[^\\/#\\?])+?",
prefix: "-",
suffix: "",
modifier: "?",
Expand Down Expand Up @@ -2597,39 +2597,6 @@ const TESTS: Test[] = [
[{ foo: "#" }, null],
],
],
/**
* https://github.com/pillarjs/path-to-regexp/issues/260
*/
[
":name*",
undefined,
[
{
name: "name",
prefix: "",
suffix: "",
modifier: "*",
pattern: "[^\\/#\\?]+?",
},
],
[["foobar", ["foobar", "foobar"]]],
[[{ name: "foobar" }, "foobar"]],
],
[
":name+",
undefined,
[
{
name: "name",
prefix: "",
suffix: "",
modifier: "+",
pattern: "[^\\/#\\?]+?",
},
],
[["foobar", ["foobar", "foobar"]]],
[[{ name: "foobar" }, "foobar"]],
],
];

/**
Expand Down
31 changes: 24 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ export interface ParseOptions {
*/
export function parse(str: string, options: ParseOptions = {}): Token[] {
const tokens = lexer(str);
const { prefixes = "./" } = options;
const defaultPattern = `[^${escapeString(options.delimiter || "/#?")}]+?`;
const { prefixes = "./", delimiter = "/#?" } = options;
const result: Token[] = [];
let key = 0;
let i = 0;
let path = "";
let backtrack = "";

const tryConsume = (type: LexToken["type"]): string | undefined => {
if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;
Expand All @@ -166,6 +166,17 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {
return result;
};

const isSafe = (value: string): boolean => {
for (const char of delimiter) if (value.includes(char)) return true;
return false;
};

const safePattern = (prefix: string, backtrack: string) => {
const prevText = prefix || backtrack;
if (!prevText || isSafe(prevText)) return `[^${escapeString(delimiter)}]+?`;
return `(?:(?!${escapeString(prevText)})[^${escapeString(delimiter)}])+?`;
};

while (i < tokens.length) {
const char = tryConsume("CHAR");
const name = tryConsume("NAME");
Expand All @@ -181,16 +192,18 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {

if (path) {
result.push(path);
backtrack = path;
path = "";
}

result.push({
name: name || key++,
prefix,
suffix: "",
pattern: pattern || defaultPattern,
pattern: pattern || safePattern(prefix, backtrack),
modifier: tryConsume("MODIFIER") || "",
});
backtrack = "";
continue;
}

Expand All @@ -202,6 +215,7 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {

if (path) {
result.push(path);
backtrack = path;
path = "";
}

Expand All @@ -216,11 +230,12 @@ export function parse(str: string, options: ParseOptions = {}): Token[] {

result.push({
name: name || (pattern ? key++ : ""),
pattern: name && !pattern ? defaultPattern : pattern,
pattern: name && !pattern ? safePattern(prefix, backtrack) : pattern,
prefix,
suffix,
modifier: tryConsume("MODIFIER") || "",
});
backtrack = suffix;
continue;
}

Expand Down Expand Up @@ -564,10 +579,12 @@ export function tokensToRegexp(
}
} else {
if (token.modifier === "+" || token.modifier === "*") {
route += `((?:${token.pattern})${token.modifier})`;
} else {
route += `(${token.pattern})${token.modifier}`;
throw new TypeError(
`Can not repeat ${token.name} with no prefix or suffix, e.g. "/:param${token.modifier}"`,
);
}

route += `(${token.pattern})${token.modifier}`;
}
} else {
route += `(?:${prefix}${suffix})${token.modifier}`;
Expand Down

0 comments on commit aa2469d

Please sign in to comment.