CVE-2026-73089

Updated on 11 Aug 2026

Severity

7.5 High severity

Details

CVSS score
7.5
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Overview

About vulnerability

Vulnerability Details

File: index.js Location: cache (browserslist()’s result cache, line ~402) and parseCache (parseQueries()’s AST cache)

Root Cause

var cache = {}
var parseCache = {}

function browserslist(queries, opts) {
...
var cacheKey = JSON.stringify([queries, context])
if (cache[cacheKey]) return cache[cacheKey]
...
if (!env.env.BROWSERSLIST_DISABLE_CACHE) { cache[cacheKey] = result }
return result
}

function parseQueries(queries) {
var cacheKey = JSON.stringify(queries)
if (cacheKey in parseCache) return parseCache[cacheKey]
var result = parseWithoutCache(QUERIES, queries)
if (!env.env.BROWSERSLIST_DISABLE_CACHE) { parseCache[cacheKey] = result }
...
}

Every distinct (queries, context) pair is cached forever — no size cap, TTL, or eviction. browserslist.clearCaches() never resets either object (it only resets node.js’s own filesystem caches); the only opt-out is the BROWSERSLIST_DISABLE_CACHE env var, controlled by the calling application, not an attacker.

Some short, valid queries amplify this badly. The since <year>-<month>-<day> query type (/^since (\d+)-(\d+)-(\d+)$/i) accepts any digit combination — Date.UTC() normalizes rather than rejects out-of-range values — giving an effectively unbounded space of ~17-byte distinct cache keys, each of which resolves to (and caches) a result close to the full ~8.5 KB browser list for any sufficiently old year.

Measured Impact

20,000 distinct since <year>-<month>-<day> queries (~330 KB total input, --expose-gc before/after measurement to rule out uncollected garbage) retained over 50 MB of heap permanently — roughly 150x amplification, growing linearly with no cap observed up to 40,000 queries (52.3 MB).

Attack Scenario

Any long-running process (server, daemon, warm CI worker) that calls browserslist() with a query value that varies across requests/items and is influenced, even partially, by external input accumulates one cache entry per distinct value ever seen. An attacker who can influence that value across many requests (this is a volumetric attack, unlike the single-request DoS findings from this same research pass) sends a stream of cheap, distinct queries (e.g. since 1900-01-01, since 1900-01-02, …) until the process runs out of memory and crashes.

Replace both plain-object caches with Maps bounded to a fixed maximum entry count, evicting the oldest entry once the cap is reached (Map preserves insertion order, so .keys().next().value is always oldest):

var CACHE_MAX_ENTRIES = 500

function boundedCacheSet(map, key, value) {
if (map.size >= CACHE_MAX_ENTRIES) {
map.delete(map.keys().next().value)
}
map.set(key, value)
}

var cache = new Map()
var parseCache = new Map()

(read sites changed to .has()/.get(), write sites to boundedCacheSet())

Verification:

  • NODE_ENV=test npx uvu test .test.js → 301/301 pass unmodified (test/cache.test.js exercises clearCaches()/BROWSERSLIST_DISABLE_CACHE against node.js’s separate filesystem caches, unaffected here); confirmed a repeated identical call still returns the cached reference.
  • Re-ran the memory PoC post-fix: heap stayed flat at ~4.9 MB after 5,000, 10,000, 20,000, and 40,000 distinct since-date queries (was 10.5 → 16.5 → 28.4 → 52.3 MB pre-fix).

Impact

  • Who is affected: Long-running processes calling browserslist() with query values that vary across requests/items and are influenced by external input.
  • What an attacker achieves: DoS via eventual out-of-memory crash, given sustained traffic over time (not a single small payload).
  • Conditions required: No authentication; requires volume rather than a single request, hence Medium rather than High severity.

Verification Environment

browserslist @ HEAD (== v4.28.6, current latest stable release) under local Node.js v20.19.5, run with --expose-gc for accurate heap measurement.

Note

Found during a broader review of this codebase in the same research pass that produced GHSA-rrmg-cfrq-23vv (parse.js algorithmic complexity), GHSA-g6p8-hj8g-x889 (baseline regexp ReDoS), GHSA-73wf-gq98-2v4g (normalizeStats crash/prototype write), and GHSA-h633-868p-5rfw (SCOPED_CONFIG__PATTERN ReDoS) — all single-request DoS vectors. This one is different in character (volumetric, not single-request) and is reported separately/scored lower accordingly.

Details

Affected product:
@0x/subproviders , @portis/web3 , @portis/web3-provider-engine , AngularJS , Babel , Next.js , React , Vue , angular-animations , angular-devkit-build-angular , angular-router , astrojs-react , autoprefixer , babel-code-frame , babel-core , babel-eslint-parser , babel-generator , babel-helper-builder-react-jsx-experimental , babel-loader , babel-parser , babel-plugin-istanbul , babel-plugin-proposal-class-properties , babel-plugin-proposal-decorators , babel-plugin-proposal-nullish-coalescing-operator , babel-plugin-proposal-numeric-separator , babel-plugin-proposal-optional-chaining , babel-plugin-syntax-async-generators , babel-plugin-syntax-export-namespace-from , babel-plugin-syntax-logical-assignment-operators , babel-plugin-syntax-numeric-separator , babel-plugin-syntax-optional-catch-binding , babel-plugin-transform-exponentiation-operator , babel-plugin-transform-flow-strip-types , babel-plugin-transform-object-assign , babel-plugin-transform-react-display-name , babel-plugin-transform-react-jsx , babel-plugin-transform-runtime , babel-polyfills , babel-preset-env , babel-preset-react , babel-preset-vue-app , babel-register , babel-runtime-corejs2 , babel-runtime-corejs3 , babel-traverse , babel-types , browserslist , browserslist-to-es-version , caniuse-api , copy-webpack-plugin , core-js , core-js-builder , core-js-bundle , core-js-pure , create-react-app , create-zephyr-apps , css-loader , css-minimizer-webpack-plugin , cssnano , cssnano-preset-advanced , cssnano-preset-default , cssnano-utils , eth-block-tracker , eth-json-rpc-infura , eth-json-rpc-middleware , expo , expo-dev-tools , expo-electron-adapter , expo-expo-cli , expo-image , expo-linking , expo-modules-core , expo-next-adapter , expo-prebuild-config , expo-screen-capture , expo-standard-web-crypto , expo-status-bar , expo-symbols , expo-webpack-config , expo-xdl , expo-yarn-workspaces , fork-ts-checker-webpack-plugin , ganache-core , gatsby , gatsby-interface , html-webpack-plugin , istanbul-lib-instrument , jest-expo-enzyme , jovidecroock-prefresh , jscodeshift , json-rpc-engine , less-loader , mini-css-extract-plugin , minimizer-webpack-plugin , modern-js-app-tools , modern-js-babel-compiler , modern-js-babel-plugin-module-resolver , modern-js-babel-preset , modern-js-core , modern-js-flight-server-transform-plugin , modern-js-plugin-i18n , modern-js-plugin-polyfill , modern-js-prod-server , modern-js-rsbuild-plugin-esbuild , modern-js-server , modern-js-server-core , modern-js-server-utils , modern-js-tsconfig , modern-js-uni-builder , modern-js-utils , modern.js , node-pixrem , nuxt , panda , pandacss-core , pandacss-dev , pandacss-generator , pandacss-mcp , pandacss-node , pandacss-parser , pandacss-postcss , pandacss-reporter , pandacss-types , postcss-colormin , postcss-convert-values , postcss-cssnext , postcss-discard-unused , postcss-loader , postcss-merge-idents , postcss-merge-longhand , postcss-merge-rules , postcss-minify-font-values , postcss-minify-params , postcss-minify-selectors , postcss-normalize-unicode , postcss-reduce-idents , postcss-reduce-initial , postcss-unique-selectors , preset-modules , rollup-plugin-zephyr , rsbuild , rsbuild-core , rsbuild-plugin-check-syntax , rsbuild-plugin-css-minimizer , rsbuild-plugin-react , rsbuild-plugin-type-check , sass-loader , sb , source-map-loader , squarelink-web3 , storybook , storybook-addon-docs , storybook-angular , storybook-builder-webpack5 , storybook-cli , storybook-codemod , storybook-core-webpack , storybook-ember , storybook-html-vite , storybook-nextjs , storybook-preact , storybook-preset-react-webpack , storybook-preset-server-webpack , storybook-react , storybook-react-dom-shim , storybook-react-vite , storybook-react-webpack5 , storybook-server , storybook-server-webpack5 , storybook-vue3 , storybook-vue3-vite , storybook-vue3-webpack5 , storybook-web-components , storybook-web-components-vite , storybook-web-components-webpack5 , style-loader , stylehacks , svgr , svgr-babel-plugin-add-jsx-attribute , svgr-babel-plugin-remove-jsx-empty-expression , svgr-babel-plugin-replace-jsx-attribute-value , svgr-babel-plugin-svg-em-dimensions , svgr-babel-plugin-transform-react-native-svg , svgr-babel-plugin-transform-svg-component , svgr-babel-preset , svgr-core , svgr-hast-util-to-babel-ast , svgr-plugin-jsx , svgr-plugin-prettier , svgr-plugin-svgo , tachyons-build-css , tachyons-generator , theme-ui , ts-loader , update-db , vite-plugin-tanstack-start-zephyr , vite-plugin-vinext-zephyr , vite-plugin-zephyr , web-infra-dev/modern.js , web3-provider-engine , web3-react , web3providerengine , webpack , webpack-dev-middleware , webpack-dev-server , webpack-subresource-integrity , with-zephyr , xdl , zephyr-agent , zephyr-astro-integration , zephyr-edge-contract , zephyr-metro-plugin , zephyr-modernjs-plugin , zephyr-nuxt-module , zephyr-packages , zephyr-repack-plugin , zephyr-rolldown-plugin , zephyr-rsbuild-plugin , zephyr-rspack-plugin , zephyr-rspress-plugin , zephyr-webpack-plugin , zephyr-xpack-internal
Affected packages:
browserslist @ 4.13.0 (+2250 more)

Fixes