mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 12:57:46 +00:00
[now-routing-utils] Fix error handling on path-to-regexp error (#3523)
This PR fixes an issue where certain patterns caused `path-to-regexp` to throw when it should be returning an error object. The fix is to make sure all inputs work properly with `path-to-regexp` before continuing.
This commit is contained in:
33
errors/invalid-route-source.md
Normal file
33
errors/invalid-route-source.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Invalid Custom Route `source`
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
When defining custom routes a route was added that causes an error during parsing. This can be due to trying to use normal `RegExp` syntax like negative lookaheads (`?!exclude`) without following `path-to-regexp`'s syntax for it.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Wrap the `RegExp` part of your `source` as an un-named parameter.
|
||||
|
||||
**Before**
|
||||
|
||||
```js
|
||||
{
|
||||
source: '/feedback/(?!general)',
|
||||
destination: '/feedback/general'
|
||||
}
|
||||
```
|
||||
|
||||
**After**
|
||||
|
||||
```js
|
||||
{
|
||||
source: '/feedback/((?!general).*)',
|
||||
destination: '/feedback/general'
|
||||
}
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [path-to-regexp](https://github.com/pillarjs/path-to-regexp/tree/v6.1.0)
|
||||
- [named parameters](https://github.com/pillarjs/path-to-regexp/blob/v6.1.0/Readme.md#named-parameters)
|
||||
- [un-named paramters](https://github.com/pillarjs/path-to-regexp/blob/v6.1.0/Readme.md#unnamed-parameters)
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
convertRedirects,
|
||||
convertHeaders,
|
||||
convertTrailingSlash,
|
||||
sourceToRegex,
|
||||
} from './superstatic';
|
||||
|
||||
export { getCleanUrls } from './superstatic';
|
||||
@@ -142,6 +143,18 @@ function checkRegexSyntax(src: string): NowErrorNested | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function checkPatternSyntax(src: string): NowErrorNested | null {
|
||||
try {
|
||||
sourceToRegex(src);
|
||||
} catch (err) {
|
||||
return {
|
||||
message: `Invalid pattern: "${src}"`,
|
||||
src,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function createNowError(
|
||||
code: string,
|
||||
msg: string,
|
||||
@@ -231,16 +244,29 @@ export function getTransformedRoutes({
|
||||
|
||||
if (typeof redirects !== 'undefined') {
|
||||
const code = 'invalid_redirects';
|
||||
const errors = redirects
|
||||
const errorsRegex = redirects
|
||||
.map(r => checkRegexSyntax(r.source))
|
||||
.filter(notEmpty);
|
||||
if (errors.length > 0) {
|
||||
if (errorsRegex.length > 0) {
|
||||
return {
|
||||
routes,
|
||||
error: createNowError(
|
||||
code,
|
||||
'Redirect `source` contains invalid regex',
|
||||
errors
|
||||
'Redirect `source` contains invalid regex. Read more: https://err.sh/now/invalid-route-source',
|
||||
errorsRegex
|
||||
),
|
||||
};
|
||||
}
|
||||
const errorsPattern = redirects
|
||||
.map(r => checkPatternSyntax(r.source))
|
||||
.filter(notEmpty);
|
||||
if (errorsPattern.length > 0) {
|
||||
return {
|
||||
routes,
|
||||
error: createNowError(
|
||||
code,
|
||||
'Redirect `source` contains invalid pattern. Read more: https://err.sh/now/invalid-route-source',
|
||||
errorsPattern
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -265,16 +291,29 @@ export function getTransformedRoutes({
|
||||
|
||||
if (typeof rewrites !== 'undefined') {
|
||||
const code = 'invalid_rewrites';
|
||||
const errors = rewrites
|
||||
const errorsRegex = rewrites
|
||||
.map(r => checkRegexSyntax(r.source))
|
||||
.filter(notEmpty);
|
||||
if (errors.length > 0) {
|
||||
if (errorsRegex.length > 0) {
|
||||
return {
|
||||
routes,
|
||||
error: createNowError(
|
||||
code,
|
||||
'Rewrites `source` contains invalid regex',
|
||||
errors
|
||||
'Rewrites `source` contains invalid regex. Read more: https://err.sh/now/invalid-route-source',
|
||||
errorsRegex
|
||||
),
|
||||
};
|
||||
}
|
||||
const errorsPattern = rewrites
|
||||
.map(r => checkPatternSyntax(r.source))
|
||||
.filter(notEmpty);
|
||||
if (errorsPattern.length > 0) {
|
||||
return {
|
||||
routes,
|
||||
error: createNowError(
|
||||
code,
|
||||
'Rewrites `source` contains invalid pattern. Read more: https://err.sh/now/invalid-route-source',
|
||||
errorsPattern
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +101,9 @@ export function convertTrailingSlash(enable: boolean, status = 308): Route[] {
|
||||
return routes;
|
||||
}
|
||||
|
||||
function sourceToRegex(source: string): { src: string; segments: string[] } {
|
||||
export function sourceToRegex(
|
||||
source: string
|
||||
): { src: string; segments: string[] } {
|
||||
const keys: Key[] = [];
|
||||
const r = pathToRegexp(source, keys, { strict: true });
|
||||
const segments = keys.map(k => k.name).filter(isString);
|
||||
|
||||
18
packages/now-routing-utils/test/index.spec.js
vendored
18
packages/now-routing-utils/test/index.spec.js
vendored
@@ -568,6 +568,15 @@ describe('getTransformedRoutes', () => {
|
||||
assert.equal(actual.error.code, 'invalid_redirects');
|
||||
});
|
||||
|
||||
test('should error when redirects is invalid pattern', () => {
|
||||
const nowConfig = {
|
||||
redirects: [{ source: '/:?', destination: '/file.html' }],
|
||||
};
|
||||
const actual = getTransformedRoutes({ nowConfig });
|
||||
assert.notEqual(actual.error, null);
|
||||
assert.equal(actual.error.code, 'invalid_redirects');
|
||||
});
|
||||
|
||||
test('should error when rewrites is invalid regex', () => {
|
||||
const nowConfig = {
|
||||
rewrites: [{ source: '^/(*.)\\.html$', destination: '/file.html' }],
|
||||
@@ -577,6 +586,15 @@ describe('getTransformedRoutes', () => {
|
||||
assert.equal(actual.error.code, 'invalid_rewrites');
|
||||
});
|
||||
|
||||
test('should error when rewrites is invalid pattern', () => {
|
||||
const nowConfig = {
|
||||
rewrites: [{ source: '/:?', destination: '/file.html' }],
|
||||
};
|
||||
const actual = getTransformedRoutes({ nowConfig });
|
||||
assert.notEqual(actual.error, null);
|
||||
assert.equal(actual.error.code, 'invalid_rewrites');
|
||||
});
|
||||
|
||||
test('should normalize all redirects before rewrites', () => {
|
||||
const nowConfig = {
|
||||
cleanUrls: true,
|
||||
|
||||
Reference in New Issue
Block a user