re2
Ecosystem: npm
Downloads:
- 0CriticalAffects 0 versions
- 0HighAffects 0 versions
- 0MediumAffects 0 versions
- 4ModerateAffects 4 versions
- 0LowAffects 0 versions
- Critical0 (0.0%)
- High0 (0.0%)
- Medium0 (0.0%)
- Moderate4 (100.0%)
- Low0 (0.0%)
Monthly breakdown
- Critical
- High
- Medium
- Moderate
- Low
- 2 vulnerabilities
- Critical: 0
- High: 0
- Medium: 0
- Moderate: 2
- Low: 0
- 2 vulnerabilities
- Critical: 0
- High: 0
- Medium: 0
- Moderate: 2
- Low: 0
Vulnerabilities (4)
Known vulnerabilities affecting re2, based on data from OSV.
| ID | Severity | Summary | Affected versions | Published | Source |
|---|---|---|---|---|---|
| GHSA-8hcv-x26h-mcgp | Moderate | 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): ```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 |
| GitHub (opens in a new tab) | |
| GHSA-j4r3-hg7j-8chg | Moderate | node-re2: Out-of-bounds heap read in `replace`/`split` via a `Buffer` ending in a truncated multi-byte UTF-8 character → adjacent heap memory disclosed to JavaScript## Summary `re2` infers a character's byte length from its UTF-8 lead byte alone, with no bound on the bytes actually remaining in the input. `Buffer` arguments reach the native layer verbatim — only strings are re-encoded into well-formed UTF-8 — so a `Buffer` whose last byte is a multi-byte lead promises continuation bytes that are not there, and the result builders read up to 3 bytes past the end of the buffer. In `replace()` and `split()` those bytes are copied into the returned `Buffer`, disclosing adjacent heap memory to JavaScript. The trigger is deterministic and requires no special h |
| GitHub (opens in a new tab) | |
| GHSA-6hxr-mr5r-9836 | Moderate | re2: Global `String.prototype.match` with an empty-matchable pattern never advances → infinite loop with unbounded native memory growth (DoS)## Summary `String.prototype.match` with a **global** `RE2` collects all matches in a native loop that advances the cursor by the match length. A **zero-width (empty) match** has length 0, so the cursor never advances: the same empty match is found forever and appended to an ever-growing native vector. Any pattern that can match the empty string (`a*`, `b?`, `x{0,3}`, `(a)|`, `(?:)`, …) therefore causes an infinite loop with unbounded memory growth. The call is synchronous native code, so it blocks the entire event loop and cannot be interrupted by `try/catch`, `AbortController`, `--max-old-s |
| GitHub (opens in a new tab) | |
| GHSA-ff84-5f28-78qj | Moderate | re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)## Summary `re2` validates the user-settable `lastIndex` against the subject's **UTF-8 byte length** but then uses it as a **UTF-16 code-unit count** to walk the subject buffer, with no bounds check. For any non-ASCII subject, the byte length is larger than the true character count, so a `lastIndex` between those two values passes validation while pointing past the end of the buffer. The subsequent walk reads out of bounds. With a large subject the read marches into unmapped memory and the process dies with **SIGABRT/SIGSEGV** — an uncatchable crash (`try/catch` cannot stop it), i.e. a denial |
| GitHub (opens in a new tab) |
- GHSA-8hcv-x26h-mcgpModeratenode-re2: String.prototype.replace(re2, template) aborts the Node process (uncatchable ToLocalChecked on empty MaybeLocal) when the result exceeds V8's max string length
< 1.25.1
## 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
CVE: CVE-2026-71430
- GHSA-j4r3-hg7j-8chgModeratenode-re2: Out-of-bounds heap read in `replace`/`split` via a `Buffer` ending in a truncated multi-byte UTF-8 character → adjacent heap memory disclosed to JavaScript
< 1.26.1
## Summary `re2` infers a character's byte length from its UTF-8 lead byte alone, with no bound on the bytes actually remaining in the input. `Buffer` arguments reach the native layer verbatim — only strings are re-encoded into well-formed UTF-8 — so a `Buffer` whose last byte is a multi-byte lead promises continuation bytes that are not there, and the result builders read up to 3 bytes past the end of the buffer. In `replace()` and `split()` those bytes are copied into the returned `Buffer`, disclosing adjacent heap memory to JavaScript. The trigger is deterministic and requires no special h
CVE: CVE-2026-71498
- GHSA-6hxr-mr5r-9836Moderatere2: Global `String.prototype.match` with an empty-matchable pattern never advances → infinite loop with unbounded native memory growth (DoS)
< 1.25.2
## Summary `String.prototype.match` with a **global** `RE2` collects all matches in a native loop that advances the cursor by the match length. A **zero-width (empty) match** has length 0, so the cursor never advances: the same empty match is found forever and appended to an ever-growing native vector. Any pattern that can match the empty string (`a*`, `b?`, `x{0,3}`, `(a)|`, `(?:)`, …) therefore causes an infinite loop with unbounded memory growth. The call is synchronous native code, so it blocks the entire event loop and cannot be interrupted by `try/catch`, `AbortController`, `--max-old-s
CVE: CVE-2026-68499
- GHSA-ff84-5f28-78qjModeratere2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)
< 1.25.2
## Summary `re2` validates the user-settable `lastIndex` against the subject's **UTF-8 byte length** but then uses it as a **UTF-16 code-unit count** to walk the subject buffer, with no bounds check. For any non-ASCII subject, the byte length is larger than the true character count, so a `lastIndex` between those two values passes validation while pointing past the end of the buffer. The subsequent walk reads out of bounds. With a large subject the read marches into unmapped memory and the process dies with **SIGABRT/SIGSEGV** — an uncatchable crash (`try/catch` cannot stop it), i.e. a denial
CVE: CVE-2026-67550