Skip to content

Commit f1253b4

Browse files
authored
Add backtrack protection to 6.x (#324)
1 parent 28a5b27 commit f1253b4

File tree

5 files changed

+184
-55
lines changed

5 files changed

+184
-55
lines changed

package-lock.json

+102
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@types/node": "^20.4.9",
3737
"@types/semver": "^7.3.1",
3838
"@vitest/coverage-v8": "^1.4.0",
39+
"recheck": "^4.4.5",
3940
"semver": "^7.3.5",
4041
"size-limit": "^11.1.2",
4142
"typescript": "^5.1.6"
@@ -46,7 +47,7 @@
4647
"size-limit": [
4748
{
4849
"path": "dist.es2015/index.js",
49-
"limit": "2 kB"
50+
"limit": "2.1 kB"
5051
}
5152
],
5253
"ts-scripts": {

redos.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { checkSync } from "recheck";
2+
import { pathToRegexp } from "./src/index.js";
3+
4+
let safe = 0;
5+
let fail = 0;
6+
7+
const tests = ["/:x{/foobar/:y}?-:z"];
8+
9+
for (const path of tests) {
10+
const regexp = pathToRegexp(path);
11+
const result = checkSync(regexp.source, regexp.flags);
12+
if (result.status === "safe") {
13+
safe++;
14+
console.log("Safe:", path, String(regexp));
15+
} else {
16+
fail++;
17+
console.log("Fail:", path, String(regexp));
18+
}
19+
}
20+
21+
console.log("Safe:", safe, "Fail:", fail);

src/index.spec.ts

+32-47
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ const TESTS: Test[] = [
13531353
prefix: ".",
13541354
suffix: "",
13551355
modifier: "+",
1356-
pattern: "[^\\/#\\?]+?",
1356+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
13571357
},
13581358
],
13591359
[
@@ -1397,7 +1397,7 @@ const TESTS: Test[] = [
13971397
prefix: ".",
13981398
suffix: "",
13991399
modifier: "",
1400-
pattern: "[^\\/#\\?]+?",
1400+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
14011401
},
14021402
".",
14031403
],
@@ -1430,13 +1430,13 @@ const TESTS: Test[] = [
14301430
prefix: ".",
14311431
suffix: "",
14321432
modifier: "",
1433-
pattern: "[^\\/#\\?]+?",
1433+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
14341434
},
14351435
],
14361436
[
14371437
["/route.html", ["/route.html", "route", "html"]],
14381438
["/route", null],
1439-
["/route.html.json", ["/route.html.json", "route", "html.json"]],
1439+
["/route.html.json", ["/route.html.json", "route.html", "json"]],
14401440
],
14411441
[
14421442
[{}, null],
@@ -1459,13 +1459,13 @@ const TESTS: Test[] = [
14591459
prefix: ".",
14601460
suffix: "",
14611461
modifier: "?",
1462-
pattern: "[^\\/#\\?]+?",
1462+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
14631463
},
14641464
],
14651465
[
14661466
["/route", ["/route", "route", undefined]],
14671467
["/route.json", ["/route.json", "route", "json"]],
1468-
["/route.json.html", ["/route.json.html", "route", "json.html"]],
1468+
["/route.json.html", ["/route.json.html", "route.json", "html"]],
14691469
],
14701470
[
14711471
[{ test: "route" }, "/route"],
@@ -1491,13 +1491,13 @@ const TESTS: Test[] = [
14911491
prefix: ".",
14921492
suffix: "",
14931493
modifier: "?",
1494-
pattern: "[^\\/#\\?]+?",
1494+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
14951495
},
14961496
],
14971497
[
14981498
["/route", ["/route", "route", undefined]],
14991499
["/route.json", ["/route.json", "route", "json"]],
1500-
["/route.json.html", ["/route.json.html", "route", "json.html"]],
1500+
["/route.json.html", ["/route.json.html", "route.json", "html"]],
15011501
],
15021502
[
15031503
[{ test: "route" }, "/route"],
@@ -2084,7 +2084,7 @@ const TESTS: Test[] = [
20842084
prefix: "",
20852085
suffix: "",
20862086
modifier: "?",
2087-
pattern: "[^\\/#\\?]+?",
2087+
pattern: "(?:(?!\\()[^\\/#\\?])+?",
20882088
},
20892089
")",
20902090
],
@@ -2290,7 +2290,7 @@ const TESTS: Test[] = [
22902290
prefix: ".",
22912291
suffix: "",
22922292
modifier: "",
2293-
pattern: "[^\\/#\\?]+?",
2293+
pattern: "(?:(?!\\.)[^\\/#\\?])+?",
22942294
},
22952295
],
22962296
[
@@ -2356,14 +2356,14 @@ const TESTS: Test[] = [
23562356
[
23572357
{
23582358
name: "foo",
2359-
pattern: "[^\\/#\\?]+?",
2359+
pattern: "(?:(?!\\$)[^\\/#\\?])+?",
23602360
prefix: "$",
23612361
suffix: "",
23622362
modifier: "",
23632363
},
23642364
{
23652365
name: "bar",
2366-
pattern: "[^\\/#\\?]+?",
2366+
pattern: "(?:(?!\\$)[^\\/#\\?])+?",
23672367
prefix: "$",
23682368
suffix: "",
23692369
modifier: "?",
@@ -2392,14 +2392,14 @@ const TESTS: Test[] = [
23922392
},
23932393
{
23942394
name: "attr2",
2395-
pattern: "[^\\/#\\?]+?",
2395+
pattern: "(?:(?!-)[^\\/#\\?])+?",
23962396
prefix: "-",
23972397
suffix: "",
23982398
modifier: "?",
23992399
},
24002400
{
24012401
name: "attr3",
2402-
pattern: "[^\\/#\\?]+?",
2402+
pattern: "(?:(?!-)[^\\/#\\?])+?",
24032403
prefix: "-",
24042404
suffix: "",
24052405
modifier: "?",
@@ -2597,39 +2597,6 @@ const TESTS: Test[] = [
25972597
[{ foo: "#" }, null],
25982598
],
25992599
],
2600-
/**
2601-
* https://github.com/pillarjs/path-to-regexp/issues/260
2602-
*/
2603-
[
2604-
":name*",
2605-
undefined,
2606-
[
2607-
{
2608-
name: "name",
2609-
prefix: "",
2610-
suffix: "",
2611-
modifier: "*",
2612-
pattern: "[^\\/#\\?]+?",
2613-
},
2614-
],
2615-
[["foobar", ["foobar", "foobar"]]],
2616-
[[{ name: "foobar" }, "foobar"]],
2617-
],
2618-
[
2619-
":name+",
2620-
undefined,
2621-
[
2622-
{
2623-
name: "name",
2624-
prefix: "",
2625-
suffix: "",
2626-
modifier: "+",
2627-
pattern: "[^\\/#\\?]+?",
2628-
},
2629-
],
2630-
[["foobar", ["foobar", "foobar"]]],
2631-
[[{ name: "foobar" }, "foobar"]],
2632-
],
26332600
];
26342601

26352602
/**
@@ -2799,6 +2766,24 @@ describe("path-to-regexp", () => {
27992766
pathToRegexp.pathToRegexp("/foo?");
28002767
}).toThrow(new TypeError("Unexpected MODIFIER at 4, expected END"));
28012768
});
2769+
2770+
it("should throw on parameters without text between them", () => {
2771+
expect(() => {
2772+
pathToRegexp.pathToRegexp("/:x:y");
2773+
}).toThrow(
2774+
new TypeError(
2775+
`Must have text between two parameters, missing text after "x"`,
2776+
),
2777+
);
2778+
});
2779+
2780+
it("should throw on unrepeatable params", () => {
2781+
expect(() => {
2782+
pathToRegexp.pathToRegexp("/foo:x*");
2783+
}).toThrow(
2784+
new TypeError(`Can not repeat "x" without a prefix and suffix`),
2785+
);
2786+
});
28022787
});
28032788

28042789
describe("tokens", () => {

0 commit comments

Comments
 (0)