Skip to content

fix: use precise regexes for transform filter to avoid backtracking#21800

Merged
sapphi-red merged 6 commits intovitejs:mainfrom
o-m12a:fix/filter-regex-stack-overflow
Mar 16, 2026
Merged

fix: use precise regexes for transform filter to avoid backtracking#21800
sapphi-red merged 6 commits intovitejs:mainfrom
o-m12a:fix/filter-regex-stack-overflow

Conversation

@o-m12a
Copy link
Copy Markdown
Contributor

@o-m12a o-m12a commented Mar 9, 2026

What this PR solves

Fixes #21696

The filter.code regexes in workerImportMetaUrl, assetImportMetaUrl, and rolldownDepPlugin used .+ (or .+?) with the /s flag. On large files (5MB+) containing new URL(...) without import.meta.url, this caused catastrophic backtracking — leading to "Maximum call stack size exceeded" or multi-second stalls.

What caused the bug

The /s flag makes . match newlines, so .+ in /new\s+URL.+import\.meta\.url/s consumes the entire remaining file after each new URL occurrence. When import.meta.url is absent, the regex engine backtracks character by character across the whole file, repeated for each new URL match.

Implementation

The filter and handler regexes are unified into a single exported regex per plugin, with dg flags and a precise pattern that requires a string literal argument ('...', "...", or `...`). Each handler creates a fresh copy via new RegExp() to avoid shared lastIndex state between concurrent async invocations.

  • assetImportMetaUrl.ts: exports assetImportMetaUrlRE, used for both filter and handler
  • workerImportMetaUrl.ts: exports workerImportMetaUrlRE, used for both filter and handler
  • rolldownDepPlugin.ts: imports assetImportMetaUrlRE for both filter and handler

Attention for reviewers

  • The regexes no longer have the /s flag, so they do not match across newlines. This is intentional — the precise patterns use character classes like [^']+ that do not need cross-line matching.
  • The regex is slightly more restrictive than the old one (requires a string literal), but this matches the behavior of the transform handler itself, so no valid cases should be missed.

Tests

Added unit tests that run the exported regexes against a large input (~2.4MB with 200 new URL(...) occurrences). Tests rely on the default test timeout to catch backtracking regressions.

o-m12a and others added 2 commits March 10, 2026 00:36
…ktracking

The filter.code regexes in workerImportMetaUrl, assetImportMetaUrl,
and rolldownDepPlugin used `.+` with the `/s` flag, which caused
catastrophic backtracking on large files containing `new URL()`
without `import.meta.url`.

Replace them with the precise regexes that require a string literal
argument, matching the patterns already used in the transform handlers.
This allows the regex to fail fast on non-matching input.

Closes vitejs#21696

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace `expect(duration).toBeLessThan(1000)` with relying on the
default test timeout to catch backtracking regressions. This avoids
flaky failures on slow CI runners.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
o-m12a and others added 4 commits March 10, 2026 18:32
lastIndex = 0 doesn't work for async handlers with await inside
the exec loop, as concurrent handler invocations can overwrite
the shared lastIndex.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@sapphi-red sapphi-red changed the title fix(optimizer): use precise regexes for transform filter to avoid backtracking fix: use precise regexes for transform filter to avoid backtracking Mar 16, 2026
@sapphi-red sapphi-red added the p3-minor-bug An edge case that only affects very specific usage (priority) label Mar 16, 2026
@sapphi-red sapphi-red merged commit dbe41bd into vitejs:main Mar 16, 2026
19 checks passed
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Mar 31, 2026
| datasource | package | from  | to    |
| ---------- | ------- | ----- | ----- |
| npm        | vite    | 7.3.1 | 8.0.3 |


## [v8.0.3](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-803-2026-03-26-small)

##### Features

- update rolldown to 1.0.0-rc.12 ([#22024](vitejs/vite#22024)) ([84164ef](vitejs/vite@84164ef))

##### Bug Fixes

- **html:** cache unfiltered CSS list to prevent missing styles across entries ([#22017](vitejs/vite#22017)) ([5464190](vitejs/vite@5464190))
- **module-runner:** handle non-ascii characters in base64 sourcemaps ([#21985](vitejs/vite#21985)) ([77c95bf](vitejs/vite@77c95bf))
- **module-runner:** skip re-import if the runner is closed ([#22020](vitejs/vite#22020)) ([ee2c2cd](vitejs/vite@ee2c2cd))
- **optimizer:** scan is not resolving sub path import if used in a glob import ([#22018](vitejs/vite#22018)) ([ddfe20d](vitejs/vite@ddfe20d))
- **ssr:** ssrTransform incorrectly rewrites `meta` identifier inside `import.meta` when a binding named `meta` exists ([#22019](vitejs/vite#22019)) ([cff5f0c](vitejs/vite@cff5f0c))

##### Miscellaneous Chores

- **deps:** bump picomatch from 4.0.3 to 4.0.4 ([#22027](vitejs/vite#22027)) ([7e56003](vitejs/vite@7e56003))

##### Tests

- **html:** add tests for `getCssFilesForChunk` ([#22016](vitejs/vite#22016)) ([43fbbf9](vitejs/vite@43fbbf9))


## [v8.0.2](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-802-2026-03-23-small)

##### Features

- update rolldown to 1.0.0-rc.11 ([#21998](vitejs/vite#21998)) ([ff91c31](vitejs/vite@ff91c31))

##### Bug Fixes

- **deps:** update all non-major dependencies ([#21988](vitejs/vite#21988)) ([9b7d150](vitejs/vite@9b7d150))

##### Miscellaneous Chores

- **deps:** update dependency [@vitejs/devtools](https://github.com/vitejs/devtools) to ^0.1.5 ([#21992](vitejs/vite#21992)) ([b2dd65b](vitejs/vite@b2dd65b))


## [v8.0.1](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-801-2026-03-19-small)

##### Features

- update rolldown to 1.0.0-rc.10 ([#21932](vitejs/vite#21932)) ([b3c067d](vitejs/vite@b3c067d))

##### Bug Fixes

- **bundled-dev:** properly disable `inlineConst` optimization ([#21865](vitejs/vite#21865)) ([6d97142](vitejs/vite@6d97142))
- **css:** lightningcss minify failed when `build.target: 'es6'` ([#21933](vitejs/vite#21933)) ([5fcce46](vitejs/vite@5fcce46))
- **deps:** update all non-major dependencies ([#21878](vitejs/vite#21878)) ([6dbbd7f](vitejs/vite@6dbbd7f))
- **dev:** always use ESM Oxc runtime ([#21829](vitejs/vite#21829)) ([d323ed7](vitejs/vite@d323ed7))
- **dev:** handle concurrent restarts in `_createServer` ([#21810](vitejs/vite#21810)) ([40bc729](vitejs/vite@40bc729))
- handle `+` symbol in package subpath exports during dep optimization ([#21886](vitejs/vite#21886)) ([86db93d](vitejs/vite@86db93d))
- improve `no-cors` request block error ([#21902](vitejs/vite#21902)) ([5ba688b](vitejs/vite@5ba688b))
- use precise regexes for transform filter to avoid backtracking ([#21800](vitejs/vite#21800)) ([dbe41bd](vitejs/vite@dbe41bd))
- **worker:** `require(json)` result should not be wrapped ([#21847](vitejs/vite#21847)) ([0672fd2](vitejs/vite@0672fd2))
- **worker:** make worker output consistent with client and SSR ([#21871](vitejs/vite#21871)) ([69454d7](vitejs/vite@69454d7))

##### Miscellaneous Chores

- add changelog rearrange script ([#21835](vitejs/vite#21835)) ([efef073](vitejs/vite@efef073))
- **deps:** bump required `@vitejs/devtools` version to 0.1+ ([#21925](vitejs/vite#21925)) ([12932f5](vitejs/vite@12932f5))
- **deps:** update rolldown-related dependencies ([#21787](vitejs/vite#21787)) ([1af1d3a](vitejs/vite@1af1d3a))
- rearrange 8.0 changelog ([8e05b61](vitejs/vite@8e05b61))
- rearrange 8.0 changelog ([#21834](vitejs/vite#21834)) ([86edeee](vitejs/vite@86edeee))


## [v8.0.0](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#800-2026-03-12)

##### Features

- update rolldown to 1.0.0-rc.9 ([#21813](vitejs/vite#21813)) ([f05be0e](vitejs/vite@f05be0e))
- warn when `vite-tsconfig-paths` plugin is detected ([#21781](vitejs/vite#21781)) ([ada493e](vitejs/vite@ada493e))

##### Bug Fixes

- **deps:** update all non-major dependencies ([#21786](vitejs/vite#21786)) ([eaa4352](vitejs/vite@eaa4352))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p3-minor-bug An edge case that only affects very specific usage (priority)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

transform.filter.code regexes with .+ and /s flag cause "Maximum call stack size exceeded" on large files

3 participants