Skip to content

Catch path-to-regexp errors and fall through to minimatch#227

Open
linpengzhang wants to merge 1 commit intovercel:mainfrom
linpengzhang:fix/catch-path-to-regexp-errors
Open

Catch path-to-regexp errors and fall through to minimatch#227
linpengzhang wants to merge 1 commit intovercel:mainfrom
linpengzhang:fix/catch-path-to-regexp-errors

Conversation

@linpengzhang
Copy link

Summary

The sourceMatches function tries path-to-regexp first and falls back to minimatch for pattern matching. However, if the source pattern uses syntax that path-to-regexp cannot parse (e.g. extglob negation like **/!(*.css|*.js)), path-to-regexp throws an exception instead of returning null. This prevents the minimatch fallback from ever running.

This PR wraps the path-to-regexp block in a try/catch so that incompatible patterns gracefully fall through to minimatch, which does support them.

The problem

// sourceMatches in src/index.js
if (allowSegments) {
    const normalized = slashed.replace('*', '(.*)');
    const expression = pathToRegExp(normalized, keys);  // throws!
    // ...
}

if (results || minimatch(resolvedPath, slashed)) {  // never reached

For a rewrite config like:

{ "source": "**/!(*.css|*.js)", "destination": "/index.html" }
  1. .replace('*', '(.*)') mangles the pattern into (.*)*/!(*.css|*.js)
  2. pathToRegExp tries to compile this and throws: Invalid regular expression: Nothing to repeat
  3. The exception propagates up — minimatch (which handles this pattern correctly) is never called
  4. The entire serveHandler() promise rejects

This caused a production incident for us where the asset server silently failed to respond to any HTTP request, because the unhandled rejection meant no response was ever sent.

The fix

Wrap lines 46-56 in a try/catch. If path-to-regexp throws, clear the keys array and fall through to the minimatch check on line 59, which is what the code already intends as a fallback.

Tests

Added an integration test that verifies a rewrite with an extglob negation pattern works correctly (previously this would have thrown).

All 67 tests pass.

Made with Cursor

The `sourceMatches` function tries path-to-regexp first and falls back
to minimatch. However, if the source pattern uses syntax that
path-to-regexp cannot parse (e.g. extglob negation like
`**/!(*.css|*.js)`), path-to-regexp throws an exception instead of
returning null. This prevents the minimatch fallback from ever running.

Wrap the path-to-regexp block in a try/catch so that incompatible
patterns gracefully fall through to minimatch, which does support them.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant