## 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` → [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: ```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.
### Summary Mermaid does not fully restrict CSS to the rendered SVG subtree. Although selectors are prefixed with `#mermaid-X`, sibling (`~` and `+`) combinators can still escape the Mermaid container and inject styles to DOM elements adjacent to the diagram `<svg>`. **Most users of mermaid would not be affected by this**, as mermaid adds its `<svg>` as an only child of it's parent element. However, you may be affected if you manually insert the `<svg>` (or other elements) into the DOM yourself. ### Details Mermaid namespaces CSS through with a middleware intended to scope all rules to the diagram's SVG element. CSS nesting expands `& ~ * { ... }` to `#svgId ~ *`, which selects all sibling elements following the SVG in the DOM, outside the diagram boundary. ### Impact An attacker able to supply diagram source to a page (e.g., user-generated content rendered by Mermaid) could inject CSS rules affecting sibling elements to the diagram `<svg>` on the host page. This can be used for UI redressing, hiding content, conditional CSS-based probing, or phishing-style visual manipulation. JavaScript execution is not possible via this vector. ### Patches This has been patched in https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed and released in [Mermaid v11.16.1](https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4011.16.1). A backport has been made for the v10 branch in 7e83f1533318b307764d961906a73377266f4c5e and was released in [Mermaid v10.9.8](https://github.com/mermaid-js/mermaid/releases/tag/v10.9.8) ### Workarounds If you are inserting the `<svg>` into the DOM yourself, you can wrap it in an element with no other children, e.g. `<div><svg>...</svg></div>` or `element.innerHTML = svg`. Alternatively, you can use `mermaid.run()` or `mermaid.initialize()` which will do this for you. Setting ["securityLevel": "sandbox"](https://mermaid.js.org/config/schema-docs/config.html#securitylevel) will also prevent this, or setting the [`secure`](https://mermaid.js.org/config/schema-docs/config.html#secure) config value in the mermaid config to avoid allowing diagrams to modify `fontFamily`, `themeCSS`, `altFontFamily`, and `themeVariables`. To test, you can try using a `themeCSS` with `& + * { /* my CSS here */}` and see if it's applied outside of your mermaid `<svg>`. ```mermaid-example --- config: themeCSS: |- & + * { background:red !important; width:100vw !important; height:100vh !important; position:fixed !important; inset:0 !important; } --- info ``` ### References - GHSA-87f9-hvmw-gh4p/CVE-2026-41159 (related vulnerability) - https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed - https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4011.16.1 - https://github.com/mermaid-js/mermaid/commit/7e83f1533318b307764d961906a73377266f4c5e - https://github.com/mermaid-js/mermaid/releases/tag/v10.9.8