GHSA-8hcv-x26h-mcgp: Medium severity npm/re2 vulnerability

Published Aug 6, 2026
·
Updated

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): cpp // 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

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 → 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:

cpp 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.

Affected Software

1 affected componentFixes available
npm/re2<=1.25.0
1.25.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/re2 to a version that resolves this vulnerability.

    Fixed in 1.25.1
  2. Upgrade

    Upgrade re2 to a version that resolves this vulnerability.

    Fixed in 1.25.1
  3. Operational

    After upgrading re2, retest the affected `String.prototype.replace` / `re2` `[Symbol.replace]` path with attacker-influenced replacement templates like `$'` (and `` $` ``) and large inputs to confirm the process no longer aborts and now throws a catchable `RangeError: Invalid string length`.

Event History

Aug 6, 2026
Advisory Published
via GitHub·09:19 PM
Data Sourced
via GitHub·09:19 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203
GHSA-8hcv-x26h-mcgp - Medium severity npm/re2 vulnerability - SecAlerts