Sep 29, 2026·7 min read·1 visit
Nodemailer versions <= 10.0.1 are vulnerable to a synchronous Denial of Service crash. Sending deeply nested arrays in recipient fields (such as 'to') causes a stack overflow in the V8 engine, crashing the application.
An uncontrolled recursion vulnerability exists in Nodemailer versions up to and including 10.0.1. When parsing recipient email addresses, recursively nested arrays bypass the parser's depth limit, resulting in V8 call stack exhaustion and immediate synchronous process termination.
Nodemailer is a widely adopted Node.js library used for handling SMTP email transfers and assembling MIME messages. The component responsible for normalizing and routing recipient lists (specifically properties such as to, cc, and bcc) is located within the MimeNode class. This class processes direct inputs containing target recipient email addresses, mapping them to standard structures before constructing the transmission envelope.\n\nThe vulnerability, cataloged under GHSA-8vvx-rff5-p5rq, lies in the lack of depth validation during the normalization and parsing of the MimeNodeAddressInput type. When an application accepts untrusted structured data directly from client payloads (e.g., via JSON APIs) and forwards it to Nodemailer without independent schema validation, the internal parsing engine is exposed to malicious structures. An attacker can supply deeply nested array structures containing a single email string, forcing the parser to descend recursively through each level of nesting.\n\nThis flaw represents a classic uncontrolled recursion scenario (CWE-674). Because the recursion relies on native JavaScript engine mechanics, standard application-level try-catch blocks and asynchronous callback exception handling are bypassed, resulting in a synchronous process termination. The attack vector requires no active SMTP connection or privileges, making any public-facing API endpoint that forwards unvalidated user-defined recipient lists to Nodemailer directly vulnerable to denial of service.
The vulnerability stems from an architectural mismatch between the type definitions and the array-flattening implementation inside MimeNode._parseAddresses(). The MimeNodeAddressInput type is recursively defined as string | MimeNodeAddress | MimeNodeAddressInput[]. This structure indicates that TypeScript allows arrays of arbitrary nesting depth to represent recipient lists. However, the runtime processing did not safely handle this theoretical depth limit.\n\nPrior to the security patch, _parseAddresses() attempted to normalize incoming addresses by calling ([] as any[]).concat(addresses). In JavaScript, the Array.prototype.concat method only flattens an array one level deep. When a multi-dimensional array nested thousands of levels deep is passed to this function, only the first outer level is flattened. The deeply nested array is then passed as an unflattened object downstream to the addressparser() utility.\n\nInside addressparser, the constructor of the internal tokenization state machine attempts to coerce the provided input into a string by calling (str || '').toString(). Because the input is a deeply nested array, JavaScript's runtime engine evaluates toString() by recursively executing Array.prototype.toString() and Array.prototype.join() for every single dimension of the nested array. Each dimension adds a frame to the V8 execution call stack, eventually exhausting the stack limit and triggering a synchronous RangeError: Maximum call stack size exceeded before any email-sending validation can occur.
The vulnerable implementation in src/mime-node/index.ts relied entirely on single-level concatenation and subsequent mapping:\n\ntypescript\n_parseAddresses(addresses: MimeNodeAddressInput | undefined): MimeNodeAddress[] {\n const flattened: MimeNodeAddress[] = [];\n ([] as any[]).concat(addresses).forEach(address => {\n if (address && address.address) {\n // ... normal processing\n return;\n }\n const parsed = this._normalizeParsedAddresses(addressparser(address));\n // ...\n });\n return flattened;\n}\n\n\nWhen a deeply nested array (e.g., [[[[['user@test.com']]]]]) is passed, concat produces [[[['user@test.com']]]]. Since this nested array does not possess an .address property, execution falls through to addressparser(address). The parser's string coercion triggers nested recursive calls through Array.prototype.toString inside V8:\n\nmermaid\ngraph LR\n A["Array.prototype.toString()"] --> B["Array.prototype.join()"]\n B --> C["Nested Array.toString()"]\n C --> D["Nested Array.join()"]\n D --> E["..."]\n\n\nTo resolve this structural flaw, the patch implemented in version 10.0.2 introduced an explicit, iterative stack-based flattening machine inside _parseAddresses(). By using a state machine and a manual stack tracking the position within each array level, the parser eliminates native recursive functions completely. In addition, a WeakSet is utilized to track visited arrays, resolving potential circular reference loops that could lead to infinite execution blocks.\n\ntypescript\n// Patched state machine logic in MimeNode._parseAddresses\nconst seen = new WeakSet<any[]>();\nconst stack: { list: any[]; pos: number }[] = [];\nconst enter = (list: any[]) => {\n if (!seen.has(list)) {\n seen.add(list);\n stack.push({ list, pos: 0 });\n }\n};\n\nenter(Array.isArray(addresses) ? addresses : [addresses]);\n\nwhile (stack.length) {\n const frame = stack[stack.length - 1];\n if (frame.pos >= frame.list.length) {\n stack.pop();\n continue;\n }\n const address = frame.list[frame.pos++];\n if (Array.isArray(address)) {\n enter(address);\n continue;\n }\n // Iterative processing continues safely...\n}\n\n\nThis state machine moves allocations from the call stack to the heap, which is significantly larger and immune to call-stack exhaustion crashes. Furthermore, the compile phase within src/mailer/index.ts was wrapped in a robust try...catch block. This prevents unexpected parser-level exceptions from causing a synchronous process crash, redirecting any errors gracefully back to the asynchronous transport callback.
Exploitation of GHSA-8vvx-rff5-p5rq requires the target application to expose an endpoint that ingests structured input and forwards it directly to Nodemailer's address fields. Modern API backends frequently utilize body-parser or native JSON parsers to deserialize incoming requests. If an endpoint accepts a parameter like to and passes it directly to transporter.sendMail(), an attacker can execute the attack without authenticating.\n\nThe payload is constructed by wrapping a valid email address inside thousands of nested array brackets. In the V8 engine, the maximum call stack size typically ranges from 10,000 to 12,000 frames. Consequently, a nested array of 5,000 to 8,000 levels is more than sufficient to crash the process. The physical payload size for such an array is minimal—approximately 10 kilobytes of JSON data—making the attack highly efficient to transmit and difficult to detect via traditional volumetric rate limits.\n\nWhen the application processes the payload, JSON.parse successfully deserializes the deeply nested array into memory. Once passed to the mailer, the execution block attempts to serialize the addresses. At this point, the JavaScript thread throws a synchronous RangeError. Because this synchronous error occurs outside the standard event-driven callback context of Node.js, the runtime immediately terminates the worker thread, causing a complete denial of service for all users hosted on that thread.
The impact of this vulnerability is assessed as Medium, with an official CVSS score of 5.9 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H). While the attack results in a complete loss of availability, the attack complexity is classified as High. This complexity is dictated by the specific application architecture required to expose the flaw: the target backend must accept deeply nested arrays and pass them directly to the email utility without intermediate type coercion or validation.\n\nIn containerized and microservice-oriented environments, a process termination can lead to a rolling denial of service. While process managers like PM2 or container orchestrators like Kubernetes will automatically restart crashed pods, the low computational cost of the exploit payload allows an attacker to continuously submit malicious requests. This sustained payload injection will overwhelm orchestration restart loops, consume significant CPU resources during startup sequences, and eventually lead to complete exhaustion of container resources.\n\nAs of the publication of this report, no active, weaponized exploits have been observed in the wild. This vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and its EPSS score is non-applicable due to the lack of a standardized CVE tracking number. However, because a reproduction proof of concept is trivial to write and distribute, the barrier to entry for developing target-specific exploits is extremely low.
The primary remediation path is upgrading the nodemailer package to version 10.0.2 or later. This update replaces the vulnerable recursive array parsing algorithm with an iterative state machine, rendering deeply nested array payloads completely harmless. Upgrading can be performed seamlessly using npm: npm install nodemailer@10.0.2 or the equivalent command in yarn or pnpm packages.\n\nIf immediate library upgrades are not feasible due to legacy dependency constraints or release freeze windows, developers must implement strict validation boundaries at the API gateway or application layer. Relying on schema validation libraries like Zod allows applications to enforce strict type constraints. By ensuring that fields like to, cc, and bcc only accept either a flat string or a flat array of strings, malicious multi-dimensional arrays are rejected at the application border:\n\ntypescript\nimport { z } from 'zod';\n\nconst SendEmailSchema = z.object({\n to: z.union([\n z.string().email(),\n z.array(z.string().email())\n ])\n});\n\n\nAdditionally, implementing a Web Application Firewall (WAF) rule to inspect payload structures can block exploit attempts before they reach the Node.js runtime. Organizations can deploy signature-based rules to detect recursive array sequences. For example, a regular expression designed to match high-frequency sequential bracket open markers can be used to drop malicious traffic at the load balancer or reverse proxy layer: (?:\\\[\\s*){10,}.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H| Attribute | Detail |
|---|---|
| CWE ID | CWE-674 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 5.9 (Medium) |
| Impact | Denial of Service (DoS) via Stack Exhaustion |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
| Vulnerable Component | MimeNode._parseAddresses / addressparser |
A DOM-based Cross-Site Scripting (XSS) vulnerability exists in the Laravel exception debug page rendering pipeline when APP_DEBUG=true is active. This flaw allows an attacker to execute arbitrary client-side JavaScript in the security context of an authenticated user's session when they hover over interactive code-trace tooltips handled by Tippy.js.
A critical denial-of-service vulnerability in the adm-zip npm package allows attackers to bypass decompression-bomb protections introduced in version 0.5.18. By declaring the uncompressed file size as exactly 0, an attacker can disable the maxOutputLength constraint in the Node.js zlib wrapper, leading to complete system memory exhaustion and process crashes.
An uncontrolled resource consumption vulnerability in the logs SDK of OpenTelemetry-Go allows remote attackers to trigger a denial of service. Under conditions of downstream exporter backpressure, the BatchingProcessor enters a tight loop, exhausting CPU resources. This occurs because the processor immediately schedules retry attempts without waiting for its ticker interval, spinning continuously when the internal queue remains filled above the batch size. The issue affects all versions prior to v0.21.0 of the go.opentelemetry.io/otel/sdk/log package.
CVE-2026-76844 is a high-severity path traversal vulnerability in webpack-dev-middleware affecting multiple version branches. It stems from an incomplete fix for CVE-2024-29180 when serving files via a physical filesystem with a non-slash-terminated publicPath configuration. Attackers can bypass directory validation to access files situated one level above the intended output directory.
A Server-Side Request Forgery (SSRF) vulnerability exists in FasterXML jackson-databind before versions 2.18.9, 2.21.5, 2.22.1, 3.1.5, and 3.2.1. The flaw occurs during the deserialization of java.net.InetAddress fields, where the library implicitly triggers eager DNS lookups. Unauthenticated remote attackers can exploit this behavior by passing arbitrary hostnames in JSON fields, forcing target servers to make outbound DNS lookup requests.
An insecure deserialization vulnerability exists in FasterXML jackson-databind due to improper validation of URI schemes when resolving java.nio.file.Path properties. When binding untrusted JSON input to a Path field, the deserializer resolves attacker-supplied URIs without restriction. If the scheme is unrecognized by the default filesystem, the application falls back to querying registered SPI FileSystemProvider instances, causing class loading and potential side effects in environments with custom providers.