Aug 7, 2026·6 min read·0 visits
Vulnerable versions of node-re2 invoke .ToLocalChecked() on empty V8 handles when string allocations fail due to length limits, crashing the entire Node.js runtime process with SIGABRT.
A denial-of-service vulnerability in node-re2 prior to version 1.25.1 allows attackers to trigger uncatchable native assertion failures in the Google V8 engine. By supplying output-amplifying replacement templates, an attacker can exceed V8 string limits, resulting in an immediate process crash.
The Node.js native regular expression binding library, node-re2, wraps the Google RE2 regular expression engine to provide safe, linear-time regular expression matching. This wrapper relies on native C++ abstractions to bridge the boundary between the V8 JavaScript engine and the RE2 C++ library. The vulnerability designated as CVE-2026-71430 resides within the replacement functionality of this native addon, specifically when handling output-amplifying replacements.
The flaw is located in the WrappedRE2::Replace implementation within lib/replace.cc. It affects both String.prototype.replace(re2, template) and RE2.prototype.replace() calls. When processing regular expression replacements, the addon allocates memory buffers and V8 string structures to hold the modified output string. This process exposes an attack surface where input and replacement parameters directly influence native memory allocations.
Under normal execution, node-re2 provides immunity to Regular Expression Denial of Service (ReDoS) because of RE2's internal DFA/NFA execution limits. However, the wrapper code itself introduces a secondary denial-of-service vector. By failing to validate the status of V8 memory allocations, the wrapper permits an uncatchable native assertion failure, terminating the entire Node.js runtime process.
The root cause of CVE-2026-71430 lies in the unsafe unwrapping of v8::MaybeLocal handles returned by the Native Abstractions for Node.js (NAN) API during allocation failures. In C++ Node.js addons, operations that instantiate JavaScript types return a v8::MaybeLocal<T> wrapper, which signals potential allocation failure by returning an empty handle. This occurs when an allocation request exceeds the engine-level restrictions, such as v8::String::kMaxLength or system memory limits.
In vulnerable versions of node-re2, the developer immediately invoked .ToLocalChecked() on the MaybeLocal instances without verifying whether the handles were empty. The .ToLocalChecked() function is designed under a fail-fast paradigm. If the underlying V8 handle is empty, .ToLocalChecked() calls v8::Utils::ReportApiFailure, which executes an uncatchable native assertion crash via abort().
Because the failure is raised within the V8 engine API itself, standard JavaScript exception handlers cannot intercept or mitigate the crash. The operating system receives a SIGABRT signal, terminating the active thread and parent Node.js process immediately with exit code 134. This makes the bug class a reachable assertion (CWE-617) rather than a standard catchable JavaScript error.
The vulnerable implementation of WrappedRE2::Replace in lib/replace.cc failed to handle allocation failures when processing replacement buffers and strings. The primary issue was the immediate execution of .ToLocalChecked() on the results of Nan::CopyBuffer and Nan::New.
// Vulnerable implementation
argv.push_back(Nan::CopyBuffer(data, item.size()).ToLocalChecked());
// ...
argv.push_back(Nan::New(data, item.size()).ToLocalChecked());
// ...
if (replacee.isBuffer)
{
info.GetReturnValue().Set(Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked());
return;
}
info.GetReturnValue().Set(Nan::New(result).ToLocalChecked());The patch implemented in version 1.25.1 refactored these calls to capture the returned v8::MaybeLocal handle first. The code now tests the handle using .IsEmpty(). If the allocation fails, the addon invokes Nan::ThrowRangeError to queue a standard JavaScript RangeError and returns early.
// Patched implementation in version 1.25.1
auto buffer = Nan::CopyBuffer(data, item.size());
if (buffer.IsEmpty())
{
Nan::ThrowRangeError("Invalid string length");
return Nan::Nothing<std::string>();
}
argv.push_back(buffer.ToLocalChecked());
auto text = Nan::New(data, item.size());
if (text.IsEmpty())
{
Nan::ThrowRangeError("Invalid string length");
return Nan::Nothing<std::string>();
}
argv.push_back(text.ToLocalChecked());This structural modification changes the outcome of an allocation failure from a process-terminating C++ assertion to a standard JavaScript exception. Since the range error is registered within the V8 context before returning, the JavaScript runtime can intercept the error via standard try-catch structures. This effectively addresses the vulnerability by preserving process availability.
To exploit CVE-2026-71430, an attacker must supply inputs to a regular expression replace operation that generate an output string exceeding the maximum string length permitted by V8. This maximum length, defined by v8::String::kMaxLength, is typically 512 MB on 32-bit platforms and 1 GB on 64-bit systems.
This length restriction can be exceeded using output-amplifying replacement templates, specifically the trailing-context selector $' and the leading-context selector `$``. These templates instruct the engine to replace each match with the remainder or precursor of the source string, respectively. If a target regular expression matches multiple characters globally throughout a long string, the output size grows quadratically relative to the input length.
For example, given an input string of 50,000 characters consisting of the character 'a', applying a global replace of 'a' with $' yields a cumulative series of substring copies. The length of the output is calculated as the sum of integers from 1 to 50,000, which is approximately 1.25 billion characters. When the addon attempts to construct the final JavaScript string containing this 1.25 GB result, V8 returns an empty handle, triggering the assertion failure and terminating the process.
The primary consequence of exploiting CVE-2026-71430 is a complete denial of service (DoS) of the Node.js runtime process. Because the crash occurs via a native SIGABRT signal, standard high-level application frameworks (such as Express, NestJS, or Koa) cannot recover from the crash, causing all active connections to drop and taking the service offline.
The CVSS v3.1 score is calculated as 6.2 (Medium) with the vector CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. The attack vector is classified as Local (AV:L) because it relies on passing arguments directly into local library APIs. However, if an application exposes regex replacement parameters or processes user-supplied template strings via node-re2 over a network interface, the practical severity escalates to a remote denial of service.
The vulnerability is categorized under CWE-617: Reachable Assertion. While the flaw does not allow remote code execution or confidential data exposure, its impact on service availability is absolute. In single-process deployments without automated orchestrators (like Kubernetes or PM2), a single request can permanently disable the application until manual intervention occurs.
The primary mitigation for CVE-2026-71430 is upgrading the re2 npm package to version 1.25.1 or later. This version introduces the necessary validation of v8::MaybeLocal allocations, converting native crashes into catchable JavaScript RangeError exceptions.
For legacy systems where immediate package updates are not feasible, applications should implement input sanitization to restrict the length of both input strings and replacement templates. Specifically, applications must reject or sanitize any user-controlled replacement templates containing the amplification characters $ followed by ' or `. Restricting maximum input lengths to values well below the V8 allocation limits (e.g., limiting inputs to less than 1 MB) prevents the quadratic expansion from reaching the threshold required to trigger the allocation failure.
Additionally, production deployments should employ robust process monitoring and orchestration tools. Systems like Kubernetes, PM2, or systemd should be configured to automatically restart crashed Node.js worker instances. While process restarts do not fix the root vulnerability, they minimize the duration of the denial of service.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
re2 uhop | < 1.25.1 | 1.25.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-617: Reachable Assertion |
| Attack Vector | Local (escalatible to Network/Remote under specific application configurations) |
| CVSS Score | 6.2 (Medium) |
| Exploit Status | Proof of Concept (PoC) verified |
| Impact | Denial of Service (DoS) via native process abort |
The program contains an assert() or similar statement that can be triggered by an attacker, leading to process termination.
The ngx-extended-pdf-viewer library embeds a version of Mozilla's pdf.js that contains vulnerability CVE-2026-16633. This vulnerability allows arbitrary JavaScript execution (XSS) upon rendering a malicious PDF file.
A medium-severity out-of-bounds (OOB) heap read vulnerability exists in node-re2 prior to version 1.26.1. When a raw binary Node.js Buffer with a truncated multi-byte UTF-8 character at its end is passed to the C++ native addon, the internal lookahead routine getUtf8CharSize() over-reads up to 3 bytes from the heap, leading to memory disclosure.
A critical OS command injection vulnerability exists in PHP_CodeSniffer's VCS blame report modules (Gitblame, Hgblame, Svnblame). Due to inadequate escaping of filenames passed to shell execution wrappers like popen(), an attacker who commits a file with a maliciously crafted name can execute arbitrary commands when the victim generates a blame report.
An authenticated stored Cross-Site Scripting (XSS) vulnerability exists in the Control Panel helper of Craft CMS before version 5.10.8. Due to lack of HTML entity encoding within the elementLabelHtml method, unescaped draft names are rendered directly into administrative interfaces.
A path traversal vulnerability exists in the local filesystem driver of Craft CMS. Due to validation occurring before path normalization, directory containment checks can be bypassed by utilizing specific protocol schemes like 'file://' along with directory traversal sequences. This allows authenticated users with administrative privileges to access or manipulate files outside the defined storage root directory.
An authorization bypass vulnerability in Craft CMS allows unauthenticated or low-privileged users to query and obtain sensitive time-series user registration counts and demographic metrics. This is due to a missing authorization check inside the actionGetNewUsersData endpoint of the ChartsController class.