Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessCraft string of 40,000+ identical chars
-
DeliverySubmit to service endpoint using re2 global replace
-
ExploitO(input²) result exceeds V8 max string length
-
ExecutionToLocalChecked() invoked on empty MaybeLocal
-
PersistNative abort() fires (SIGABRT)
-
ImpactNode.js process terminates, all requests dropped
Vulnerability Assessment
| Weakness |
CWE
ASVS 5.0
V16 - Security Logging and Error Handling Reachable assertion handling
|
| Exploitation Conditions | Three conditions must all hold simultaneously: (1) The application uses the |
| Impact | The provided CVSS vector (AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H, score 6.2) conflicts directly with the advisory description, which explicitly states 'a remote, unauthenticated denial of service against any service' where attacker-controlled input reaches the replace path. AV:L (local access) underrepresents the realistic threat model: any Internet-facing Node.js service that feeds user-supplied strings into |
| Exploit ScenarioDerived | An attacker sends a POST request to a content-processing API endpoint where the backend calls |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Aug 06, 2026
Process-terminating denial of service in node-re2 (npm package re2) up to and including v1.25.0 allows an attacker to kill the entire Node.js process or worker thread with a single crafted request. The native add-on's WrappedRE2::Replace calls .ToLocalChecked() on a V8 MaybeLocal without first verifying it is non-empty; when a global replace uses the $' (post-match) or ` $ ` (pre-match) template on ~40,000+ matching characters, the O(input²) output exceeds V8's 536,870,888-character limit, the MaybeLocal is empty, and the unchecked call triggers v8::Utils::ReportApiFailure → native abort() (SIGABRT, exit 134). Unlike the built-in regex engine, which raises a catchable RangeError, this crash cannot be intercepted by JavaScript try/catch, domains, or uncaughtException` handlers, making a single malformed request capable of taking down the entire service. A public proof-of-concept is included in the GitHub Security Advisory; no CISA KEV listing was present at time of analysis.
RemediationAI
Upgrade the re2 npm package to version 1.25.1 or later, which resolves the issue by checking the MaybeLocal return value on all result paths in WrappedRE2::Replace (both the buffer path at L553 and the string path at L556) and throwing a catchable RangeError: Invalid string length instead of aborting - matching the behavior of the built-in engine. The upgrade is non-breaking and requires no API changes: npm install re2@>=1.25.1 or npm upgrade re2 in any project using node-re2 is sufficient. If an immediate upgrade is not feasible, two compensating controls apply with meaningful trade-offs: (1) Audit all call sites for re2.replace with global flags and replace $' or ` $ ` templates with $&` or literal strings - this eliminates the amplification path but may require logic changes if post/pre-match capture was intentional; (2) Enforce application-layer input length limits (e.g., reject strings exceeding 30,000 characters before they reach the replace path) - this prevents the threshold from being reached but requires accurate knowledge of all entry points and does not fix the underlying defect. The vendor advisory is at https://github.com/uhop/node-re2/security/advisories/GHSA-8hcv-x26h-mcgp.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 2 packages in npm, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
node-re2 (CPE: pkg:npm/re2) is a Node.js native add-on wrapping Google's RE2 library via the Nan (Native Abstractions for Node.js) C++ bridge. The vulnerability is in lib/replace.cc at the point where the computed replacement string is returned to V8. V8 imposes a hard maximum string length (~536,870,888 characters on 64-bit platforms), and when Nan::New(result) is called with a string exceeding this limit, it returns an empty MaybeLocal<v8::String> to signal failure. The Nan convention is to check .IsEmpty() before calling .ToLocalChecked(); skipping this check and calling .ToLocalChecked() on an empty MaybeLocal invokes v8::Utils::ReportApiFailure which unconditionally calls abort(). CWE-617 (Reachable Assertion) captures this class: a runtime invariant assumed by the code is violated, and the failure path is a process-terminating trap rather than a graceful error. The amplification arises from $' and ` $ ` back-references: $' appends the entire post-match suffix to each replacement slot, so on a string of N identical characters with a global single-char pattern, the aggregate output approaches N²/2 characters. At N≈40,000 this surpasses V8's ceiling. $&`, literal, and numbered group templates produce O(N) output and are unaffected.
Know when Node.js gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
AV:N because the advisory explicitly confirms remote unauthenticated exploitation via network input; the provided AV:L conflicts with this and appears to be a scoring error.
Primary rating from Vendor (https://github.com/uhop/node-re2).
CVSS VectorVendor: https://github.com/uhop/node-re2
Lifecycle Timeline
1DescriptionCVE.org
Description
WrappedRE2::Replace builds the replacement result and hands it to V8 with .ToLocalChecked() without checking for the empty MaybeLocal that V8 returns when the string/buffer exceeds its maximum length:
lib/replace.cc (v1.24.1):
// L553 - Buffer return path
info.GetReturnValue().Set(Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked());
// L556 - String return path
info.GetReturnValue().Set(Nan::New(result).ToLocalChecked());When a global replace uses an output-amplifying template - $' (text after the match) or ` $ ` (text before the match) - the result grows to O(input²). For an input of ~40,000+ identical single-char matches the result exceeds V8's String::kMaxLength (~536,870,888 chars on 64-bit). Nan::New(result) then returns an empty MaybeLocal, and the unchecked .ToLocalChecked() calls v8::Utils::ReportApiFailure → FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal → abort()` (SIGABRT).
This is an uncatchable crash: it is not a JavaScript exception, so a surrounding try/catch cannot stop it - the entire Node process (or worker) dies.
The built-in regex engine handles the identical case correctly by throwing a *catchable* RangeError: Invalid string length. node-re2 diverges from that contract and aborts instead.
Proof of concept
npm i re2
node poc.jsconst RE2 = require('re2');
// Built-in engine: same case -> CATCHABLE RangeError (correct)
try { 'a'.repeat(50000).replace(/a/g, "$'"); }
catch (e) { console.log('native:', e.constructor.name, e.message); } // RangeError: Invalid string length
// re2: ABORTS the whole process (uncatchable; try/catch does not help)
'a'.repeat(50000).replace(new RE2('a', 'g'), "$'");
// -> FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal (process exits 134 / SIGABRT)Observed (Node v24, clean npm i re2 → re2@1.24.1): native branch prints RangeError: Invalid string length; the re2 branch aborts with FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal, stack top WrappedRE2::Replace, process exit code 134.
Threshold matches the mechanism precisely: input of 30,000 chars completes; 40,000 aborts (30000²/2 ≈ 4.5e8 < 5.37e8 max; 40000²/2 ≈ 8e8 > max). $&/constant templates and non-global replaces do not amplify and do not crash.
Impact
A remote, unauthenticated denial of service against any service that runs String.prototype.replace / the re2 [Symbol.replace] path where either the replacement template (containing $' or ` $ `) or the input size is attacker-influenced. Because the failure is a native abort(), it cannot be contained by try/catch` or domains - one request takes down the whole process/worker. This is especially impactful for re2's core audience, who adopt it specifically to process untrusted patterns/inputs safely.
Suggested fix
Check the MaybeLocal before ToLocalChecked on both return paths (and the intermediate group-string builds), and throw a catchable RangeError to match the built-in engine:
auto maybe = Nan::New(result);
if (maybe.IsEmpty()) { Nan::ThrowRangeError("Invalid string length"); return; }
info.GetReturnValue().Set(maybe.ToLocalChecked());(Apply equivalently to the Nan::CopyBuffer(...) buffer path at L553 and to the per-group Nan::New(data, size).ToLocalChecked() sites used by the replacer-function path.)
Resolution
Resolved in re2 1.25.1. WrappedRE2::Replace now checks the returned MaybeLocal on every result path and throws a catchable RangeError: Invalid string length (matching the built-in engine) instead of aborting the process with an uncatchable SIGABRT. No API changes --- upgrade to re2 >= 1.25.1 via a plain npm upgrade to receive the fix.
Blast Radius
ecosystem impact- 1 npm packages depend on re2 (1 direct, 0 indirect)
Ecosystem-wide dependent count for version 1.25.1.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.
Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation
A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could
FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote
Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour
Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t
Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete
Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc
An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi
Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin
The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic
EUVD-2026-54159
GHSA-8hcv-x26h-mcgp
NVD
CVE.org
EPSS
Vulnrichment
VulDB
Exploit-DB