Sep 1, 2026·7 min read·4 visits
Unbounded global caching in browserslist allows remote attackers to trigger process-wide Denial of Service via crafted browser query parameters that bypass conventional memory limitations.
An uncontrolled resource consumption vulnerability (CWE-770) in the browserslist NPM package prior to version 4.28.7 allows remote unauthenticated attackers to cause an Out-Of-Memory (OOM) application crash. Because the in-memory cache implementation lacks limits or eviction logic, supplying unique browser queries triggers linear heap memory growth.
The package browserslist is an open-source library that facilitates the sharing of target browser configurations and Node.js environments among modern front-end build utilities, including Autoprefixer, Babel, and Stylelint. By resolving queries such as > 1%, last 2 versions, it generates a standardized list of target user agents. This resolution mechanism makes browserslist a fundamental block in contemporary web development pipelines and server-side compilation tools.
Because server-side environments, online playgrounds, and software-as-a-service (SaaS) continuous integration systems frequently execute configuration processing dynamically, the exposed API is an attractive target for resource exhaustion attacks. The vulnerability tracked as CVE-2026-73089 represents a high-severity uncontrolled resource consumption flaw. Under specific conditions where an application passes user-influenced string parameters directly into the compilation module, remote attackers can trigger an application-wide Denial of Service.
Specifically, the library implements an in-memory cache mechanism designed to avoid repetitive query parsing. However, prior to version 4.28.7, this caching logic lacked size limitations, expiration criteria, or eviction policies. The vulnerability is classified under CWE-770 (Allocation of Resources Without Limits or Throttling) and carries a CVSS base score of 7.5, indicating severe implications for application availability.
The vulnerability stems from the implementation of global cache storage mechanisms within index.js. Two distinct global variables, cache and parseCache, are declared as plain JavaScript objects. In JavaScript, standard object literals do not restrict the number of keys they hold, nor do they natively support automatic memory reclamation or entry eviction. Consequently, every unique query resolved by the application adds a key-value pair that remains in memory for the duration of the Node.js process.
When a query executes, the library computes a cache key by serializing the query array and context object using JSON.stringify(). If the computed key is missing from the global object, the library parses the string and stores the resulting array. This stored value represents hundreds of target user agent strings, consuming several kilobytes per entry. This architecture functions correctly for small, static configurations but breaks down when confronted with dynamic or algorithmic inputs.
An auxiliary factor that amplifies this vulnerability is how the parser handles date-based queries. The library processes queries structured as since <year>-<month>-<day> by converting arguments to timestamp values via the JavaScript Date.UTC() method. Crucially, Date.UTC() exhibits permissive parsing behavior by normalizing out-of-bounds parameters instead of throwing runtime errors. For example, passing day 32 or month 13 transitions logically to the subsequent period. This permissive parsing enables an attacker to feed an infinite sequence of distinct, structurally valid date inputs, causing the engine to generate unique cache entries on every execution.
To understand the structural flaws, we can examine the vulnerable code path inside index.js. The initialization of the global state relies on unrestricted object declarations:
var cache = {}
var parseCache = {}During the execution of the main resolution loop, the library searches for the computed key within the cache object. If the key is not present, the target queries are resolved, and the output is appended to the object without any validation of the total cache size:
var cacheKey = JSON.stringify([queries, context])
if (cache[cacheKey]) return cache[cacheKey]
// ... query resolution occurs ...
if (!env.env.BROWSERSLIST_DISABLE_CACHE) {
cache[cacheKey] = result
}In version 4.28.7, the developers replaced the plain object cache with an ES6 Map instance and introduced a strict cap on the maximum number of cached elements, set via CACHE_MAX_ENTRIES to 500:
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()This implementation implements a First-In, First-Out (FIFO) eviction strategy. Because ES6 Map objects preserve the insertion order of keys, calling map.keys().next().value retrieves the oldest entry. Deleting this oldest key ensures that the cache size never exceeds the 500-entry limit, neutralizing the unbounded growth vector. The query evaluation checks then use map.has(key) and map.get(key) instead of property access.
Exploitation of CVE-2026-73089 requires an application environment where the browserslist parser receives untrusted inputs. This scenario typically exists in build servers, online code compilers, front-end optimization platforms, and tools that compile CSS or JavaScript assets on behalf of tenants. If the application exposes an API endpoint where users specify their target browser requirements, the attack surface is active.
The attack begins when an attacker transmits a large volume of HTTP requests containing unique query strings. By dynamically altering the date values in a since YYYY-MM-DD query structure, the attacker ensures that every request misses the existing cache. Because the parser accepts highly arbitrary values without throwing exceptions, the attacker can systematically cycle through dates (e.g., since 1990-01-01 through since 1990-01-20000).
As the server-side process executes each query, the memory footprint increases. Each cache entry stores not only the key but also the resolved array of user agent strings, representing substantial heap allocations. Because the global cache object maintains active references to these arrays, the Node.js Garbage Collector cannot reclaim the allocated memory. This leads to a steady, linear escalation of memory consumption. Eventually, the V8 engine reaches its maximum configured heap limit, halting execution with an Out-Of-Memory error and crashing the application host.
Below is a horizontal representation of the data flow and memory growth pathway during exploitation:
The primary impact of this vulnerability is a complete Denial of Service. When the Node.js process exceeds its available memory, the runtime crashes, terminating active user connections and disrupting all dependent services. In environments without robust process supervisors (such as systemd, PM2, or Kubernetes pod restarts), the server remains offline indefinitely, requiring manual administrative intervention to restore availability.
From a CVSS v3.1 perspective, the vulnerability is scored at 7.5 (High Severity) with a vector of CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. The attack vector is Network, meaning it can be exploited remotely without authentication or user interaction. Although it does not violate data confidentiality or system integrity, the high impact on availability presents a severe threat, particularly to multi-tenant SaaS platforms where a single malicious payload can take down shared backend containers.
The Exploit Prediction Scoring System (EPSS) reports a low probability of active exploitation in the wild (0.36%). However, because the library is a foundational dependency inside millions of Node.js projects, the practical exposure is extensive. Security teams must treat the vulnerability as a priority issue if they host services that process user-controlled compilation configurations.
The definitive remediation for CVE-2026-73089 is upgrading the browserslist package to version 4.28.7 or higher. Organizations using package managers should run updates to verify that all sub-dependencies resolve to the patched version, as many build systems pull in browserslist indirectly through Babel or Autoprefixer:
npm update browserslistFor systems where immediate upgrades are constrained by legacy dependencies or deployment cycles, a reliable workaround is available. Setting the environment variable BROWSERSLIST_DISABLE_CACHE=1 completely deactivates both the query and AST parse caches. While this setting increases CPU overhead because it forces re-parsing of queries on every invocation, it eliminates the heap memory growth vector and protects against Denial of Service.
Additionally, developers can implement input sanitization to block dynamic queries. By enforcing a strict regular expression whitelist on browser query parameters, applications can prevent the parsing of arbitrary date strings. Restricting input to predefined configurations ensures that cache keys remain within a highly predictable, finite boundary.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
browserslist browserslist | < 4.28.7 | 4.28.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-770 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 7.5 (High) |
| EPSS Score | 0.0036 (Percentile: 28.92%) |
| Impact | Denial of Service (OOM Crash) |
| Exploit Status | PoC (Validation Suite) |
| KEV Status | Not Listed |
The software allocates memory resources for caching query strings and abstract syntax trees (ASTs) without setting limits, size caps, or throttling mechanisms on the number of cached items, causing eventual exhaustion of heap space.
An algorithmic complexity vulnerability in the pypdf library before version 6.16.1 allows remote or local attackers to cause an application denial of service. The flaw is triggered via maliciously crafted PDF documents that utilize either deeply nested outlines or exponential Directed Acyclic Graph (DAG) structures in Form XObjects.
An authentication bypass vulnerability exists in Filament's app-based (TOTP/authenticator) multi-factor authentication (MFA) system when recovery codes are enabled. This allow attackers possessing primary credentials to bypass the second-factor authentication check entirely by manipulating the Livewire state during the challenge-form validation lifecycle.
An authentication oracle vulnerability exists in Filament before 4.12.5 and 5.7.5. The application initiates MFA challenge workflows prior to verifying user authorization policies, allowing unauthenticated attackers to validate guessed credentials.
A multi-factor authentication bypass vulnerability exists in Filament (Laravel full-stack framework panels) due to improper time-step tracking of Time-Based One-Time Password (TOTP) codes. By submitting valid TOTP codes from an older time window within the drift allowance, an attacker with a user's password can bypass the single-use MFA guarantee and obtain unauthorized account access.
CVE-2026-19418 is a high-severity origin validation vulnerability in TYPO3 CMS that enables Cross-Site Request Forgery (CSRF) and access control bypasses. Due to architectural consolidation of entry points in version 13.0, the core ReferrerEnforcer fails to isolate backend endpoints from the frontend, allowing an attacker with frontend script execution capabilities to perform unauthorized administrative actions.
CVE-2026-84304 is a high-severity uncontrolled resource consumption vulnerability in gRPC-Go, the Go implementation of the gRPC framework. The issue stems from a memory amplification flaw inside the HTTP/2 DATA frame processing subsystem. Remote, unauthenticated attackers can exploit this vulnerability by sending a high volume of heavily fragmented, tiny DATA frames within multiplexed concurrent streams. This causes gRPC-Go servers to allocate excessive internal metadata structures on the Go heap, leading to severe heap memory amplification, intense garbage collection thrashing, and process termination due to Out-of-Memory (OOM) conditions.