CVE-2026-71430
node-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
6.2
node-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
CVE-2026-71430- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-617
- Updated
- 2026-11-02
EUVDEUVD-2026-54159
6.2
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
EUVD-2026-54159- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8hcv-x26h-mcgp
6.2
node-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
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.js
const 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 → [email protected]): 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.
- Record
GHSA-8hcv-x26h-mcgp- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-617
- Updated
- 2026-11-02
NVD
6.2
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
CVE-2026-71430- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-617
- Updated
- 2026-11-02
OSV
6.2
node-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
CVE-2026-71430- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-617
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-71430
6.2
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
DEBIAN-CVE-2026-71430- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVGHSA-8hcv-x26h-mcgp
6.2
node-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
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.js
const 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 → [email protected]): 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.
- Record
GHSA-8hcv-x26h-mcgp- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-617
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-71430
6.2
node-re2 provides RE2 regular expression bindings for Node.js. Prior to version 1.25.1, the WrappedRE2::Replace function built its replacement result and passed it to V8 using ToLocalChecked without checking for the empty MaybeLocal that V8 returns when the resulting string or buffer exceeds V8's maximum string length. When a global replace uses an output amplifying replacement template, the result can grow quadratically with the input size, and once the result exceeds V8's maximum string length, the unchecked ToLocalChecked call causes a fatal, uncatchable process abort instead of a catchable exception. This issue is fixed in version 1.25.1.
- Record
UBUNTU-CVE-2026-71430- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
re2npm |
through 1.25.0 | 1.25.1 |
node-re2Debian:12 |
All versions, plus listed versions 1.18.0+~cs2.13.8-11.19.2+~cs2.13.13-11.20.10+~cs2.13.13-11.20.12+~cs2.13.13-11.21.4+~cs2.13.13-1 Show 8 moreShow less1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-11.26.1+~cs1.8.0-11.27.0+~cs1.8.1-1 |
None known |
node-re2Debian:13 |
All versions, plus listed versions 1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-11.26.1+~cs1.8.0-11.27.0+~cs1.8.1-1 |
None known |
node-re2Debian:14 |
before 1.26.1+~cs1.7.0-1, plus listed versions 1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-2 |
1.26.1+~cs1.7.0-1 |
re2npm |
before 1.25.1 | 1.25.1 |
node-re2Ubuntu:20.04:LTS |
All versions, plus listed versions 1.10.4-11.10.5-11.10.5-1build1 |
None known |
node-re2Ubuntu:22.04:LTS |
All versions, plus listed versions 1.15.9+~cs1.8.18-11.15.9+~cs1.8.18-21.16.0+~cs2.12.8-21.17.1+~cs2.12.8-11.17.2+~cs2.13.8-11.17.3+~cs2.13.8-1 |
None known |
node-re2Ubuntu:24.04:LTS |
All versions, plus listed versions 1.18.0+~cs2.13.8-1build11.19.2+~cs2.13.13-1build11.20.10+~cs2.13.13-1 |
None known |
node-re2Ubuntu:26.10 |
All versions, plus listed versions 1.23.3+~cs1.3.5-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-1 |
None known |
node-re2Ubuntu:26.04:LTS |
All versions, plus listed versions 1.22.1+~cs1.3.5-11.22.1+~cs1.3.5-1build21.22.1+~cs1.3.5-1build31.23.3+~cs1.3.5-1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-71430 is a medium vulnerability (CVSS 6.2) in Uhop Node-re2, published August 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.16%, the 4th percentile. Fixed in re2 1.25.1.
References
github.com/uhop/node-re2/security/advisories/GHSA-8hcv-x26h-mcgp
github.com/advisories/GHSA-8hcv-x26h-mcgp
github.com/CVEProject/cvelistV5/tree/main/cves/2026/71xxx/CVE-2026-71430.json
nvd.nist.gov/vuln/detail/CVE-2026-71430
security-tracker.debian.org/tracker/CVE-2026-71430
github.com/uhop/node-re2
ubuntu.com/security/CVE-2026-71430
cve.org/CVERecord?id=CVE-2026-71430