KEVWire

Security intelligence, with context.

Response snapshot Oct 27, 2026, 07:22 UTC

Back to the wire
Permalink
VulnerabilityCVE-2026-110871

Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing

When nodemailer DKIM-signs an outbound message, its DKIM message parser unfolds RFC 5322 header continuation lines with a loop that concatenates each continuation line onto the preceding line and then re-tests the anchored regex `/^[ \t]/` against the growing merged line on every iteration.

At a glance

Exploited
Not known
Severity
6.5 Medium (CVSS 3.1)
Chance of exploitation
0.4% in the next 30 days
Sources
2 independent sources

What to do

GitHub lists first patched versions: nodemailer: 10.0.10. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-39m8-27wv-hr27 (opens a new tab)

What happened

## Summary

When nodemailer DKIM-signs an outbound message, its DKIM message parser unfolds RFC 5322 header continuation lines with a loop that concatenates each continuation line onto the preceding line and then re-tests the anchored regex `/^[ \t]/` against the growing merged line on every iteration.

Re-testing the regex against the ever-growing accumulated line forces a full string flatten proportional to the accumulated length on each iteration, yielding O(n^2) CPU in the number of continuation lines.

An application user who influences the size of a header that folds into many continuation lines (most naturally a large recipient list, but also a long custom header, Subject, or References) can drive tens of thousands of continuation lines and block the single-threaded Node.js event loop for seconds during signing, a repeatable denial of service.

Continue reading

## Root Cause

The invariant that signing a message performs work linear in the message size is violated in the header-unfolding step. In `src/dkim/message-parser.ts`, `parseHeaders()` splits the buffered header block into `lines` and iterates backward; for each line matching the continuation pattern `/^[ \t]/`, it does `lines[i - 1] += '\n' + lines[i]` and `lines.splice(i, 1)`.

The dominant cost is not the array splice: iterating backward keeps each splice near the shrinking tail, so it shifts only the few trailing header lines. The quadratic cost is the anchored regex test `/^[ \t]/.test(lines[i])` (together with the `+=` concatenation) evaluated against the growing merged line on every iteration.

The `+=` builds an O(1) rope, but the next iteration's regex test forces a full flatten of the entire accumulated line, so the total cost is the sum over all continuation lines, which is O(n^2).

The header block is buffered by `checkHeaders` without any size or line-count cap; the DKIM signer's `MAX_MESSAGE_SIZE` and `cacheTreshold` knobs gate the body stream only, not the header block, so nothing bounds the number of continuation lines fed to the loop.

```text Affected file and function (verified at the published v10.0.9 artifact, in src/ and compiled verbatim into the published dist output): - src/dkim/message-parser.ts: MessageParser.parseHeaders (the continuation-unfold loop that re-tests /^[ \t]/ against the growing merged line) ```

## Impact

An attacker or user who can make an outbound, DKIM-signed message carry a header that folds into a large number of continuation lines can freeze the sending Node.js process during signing. Because Node.js is single-threaded, the synchronous unfold loop starves the entire event loop, halting all other work in the process, and it is repeatable on every such message.

The natural lever is a large recipient list: nodemailer's own MIME composer folds a To or Cc value at column 76 into space-prefixed continuation lines, and the count scales linearly with the number of recipients, so a caller-supplied recipient list (common in bulk-mail, notification, and multi-tenant email APIs) turns into tens of thousands of continuation lines.

The single precondition is that DKIM signing is enabled, which is a mainstream deliverability configuration. Independent reproduction on Node.js 24 measured roughly 2.8 seconds of event-loop blocking at about 20,000 continuation lines and roughly 11.5 seconds at about 40,000, scaling quadratically; the full composer-to-signer chain with a 60,000-recipient To header blocked for roughly 6.6 seconds.

The applications that cap recipient count or header size shrink the exposed population but do not change the per-message availability impact once a large header is accepted.

## Proof of Concept

`Node.js 24; the shipped DKIM signer (require('nodemailer/lib/dkim')) or the exact unfold loop.`

Steps: (1) construct a message whose header block contains one header value folded into many space-prefixed continuation lines (for example by supplying a very large recipient list, which nodemailer's MIME composer folds automatically); (2) DKIM-sign it and observe the event loop stall while `parseHeaders` unfolds the block. A minimal microbenchmark of the exact unfold logic demonstrates the complexity:

```js // Reproduces the parseHeaders unfold loop verbatim (Node 24). function unfold(headerBlock) { const lines = headerBlock.split(/\r?\n/); for (let i = lines.length - 1; i > 0; i--) { if (/^[ \t]/.test(lines[i])) { lines[i - 1] += '\n' + lines[i]; lines.splice(i, 1); } } return lines; } function run(n) { let block = 'To: a@b.c\r\n'; for (let i = 0; i < n; i++) block += ' cont-' + i + '@example.

com,\r\n'; for (let k = 0; k < 5; k++) block += 'X-H' + k + ': v\r\n'; const t = Date.now(); unfold(block); return Date.now() - t; } for (const n of [5000, 10000, 20000, 40000]) console.log(n, run(n) + 'ms'); ```

```text 5000 27ms 10000 542ms 20000 2853ms 40000 11503ms (2x input yields roughly 4x-5x time: quadratic. The cost is the regex re-flatten of the growing merged line, not the splice.) ```

## Attack Chain

1. **Exposure:** An application DKIM-signs outbound mail (a mainstream deliverability configuration, enabled per transport or per message) for a message whose header size is influenced by a user, for example a large recipient list, a long custom header, Subject, or References. 2. **Control:** The attacker or user controls the size of that header.

The MIME composer folds it deterministically at column 76 into space-prefixed continuation lines that match the continuation pattern, so the continuation-line count scales linearly with the supplied size. 3. **Path:** Signing pipes the composed message through the DKIM MessageParser; `checkHeaders` buffers the whole header block into `rawHeaders` and calls `parseHeaders()`, the single sink, on every signed message.

4. **Guard:** No header-size or line-count cap exists between the input and the sink; the signer's body-only size thresholds do not apply to the header block. 5. **Primitive:** The unfold loop re-tests the anchored regex against th

Affected package ranges reported by GitHub: nodemailer: >= 3.0.0, <= 10.0.9

Scores and how they were measured

CVSS base severity

6.5

Version 3.1As of

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

Measurement source (opens a new tab)

How bad it is if exploited. Not a prediction of exploitation or proof you are exposed.

EPSS probability

0.4%

Scored Percentile 31.8%

Model and measurement source (opens a new tab)

A model's estimate of exploitation in the next 30 days, not certainty or your own risk.

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-39m8-27wv-hr27 (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  3. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  4. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  5. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  6. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  7. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  8. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  9. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  10. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  11. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-39m8-27wv-hr27, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  12. Primary source

    NVD CVE-2026-110871 (opens a new tab)

    NVD record: description, CVSS (the CNA score is preferred), affected products, and publication date. Not evidence of exploitation.

    Published Checked

  13. Primary source

    NVD CVE API 2.0 (opens a new tab)

    The NVD API page that supplied this CVE record.

    Published Date not suppliedChecked

  14. Primary source

    FIRST measured EPSS score (opens a new tab)

    Measured EPSS probability and percentile for CVE-2026-110871, scored 2026-10-26. This is a model estimate, not proof of exposure.

    Published Checked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, October 22). Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing (CVE-2026-110871). https://kevwire.com/record/CVE-2026-110871

MLA

"Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing (CVE-2026-110871)." KEVWire, 22 Oct. 2026, kevwire.com/record/CVE-2026-110871.

Chicago

KEVWire. "Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing (CVE-2026-110871)." Last modified October 22, 2026. https://kevwire.com/record/CVE-2026-110871.

Evidence summary (plain text)

Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing (Oct 22, 2026).
When nodemailer DKIM-signs an outbound message, its DKIM message parser unfolds RFC 5322 header continuation lines with a loop that concatenates each continuation line onto the preceding line and then re-tests the anchored regex `/^[ \t]/` against the growing merged line on every iteration. Re-testing the regex against the ever-growing accumulated line forces a full string flatten proportional to the accumulated length on each iteration, yielding O(n^2) CPU in the number of continuation lines. An application user who influences the size of a header that folds into many continuation lines (most naturally a large recipient list, but also a long custom header, Subject, or References) can drive tens of thousands of continuation lines and block the single-threaded Node.js event loop for seconds during signing, a repeatable denial of service.

The invariant that signing a message performs work linear in the message size is violated in the header-unfolding step. In `src/dkim/message-parser.ts`, `parseHeaders()` splits the buffered header block into `lines` and iterates backward; for each line matching the continuation pattern `/^[ \t]/`, it does `lines[i - 1] += '\n' + lines[i]` and `lines.splice(i, 1)`. The dominant cost is not the array splice: iterating backward keeps each splice near the shrinking tail, so it shifts only the few trailing header lines. The quadratic cost is the anchored regex test `/^[ \t]/.test(lines[i])` (together with the `+=` concatenation) evaluated against the growing merged line on every iteration. The `+=` builds an O(1) rope, but the next iteration's regex test forces a full flatten of the entire accumulated line, so the total cost is the sum over all continuation lines, which is O(n^2). The header block is buffered by `checkHeaders` without any size or line-count cap; the DKIM signer's `MAX_MESSAGE_SIZE` and `cacheTreshold` knobs gate the body stream only, not the header block, so nothing bounds the number of continuation lines fed to the loop.
CVSS 6.5 (version 3.1; evidence as of Oct 22, 2026; https://github.com/advisories/GHSA-39m8-27wv-hr27).
EPSS 0.4% for the following 30 days, scored Oct 26, 2026 (https://api.first.org/data/v1/epss?cve=CVE-2026-110871&date=2026-10-26).
Sources:
GitHub-reviewed advisory GHSA-39m8-27wv-hr27: https://github.com/advisories/GHSA-39m8-27wv-hr27
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo1MTozMFrOAAf0Mg%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0OToxMlrOAAf0LQ%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0NzoxN1rOAAf0Kg%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0NTozNlrOAAf0Jw%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0NTowNVrOAAf0JQ%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0Mzo0NVrOAAf0Iw%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QyMDo0MzozMVrOAAf0Ig%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QxODozNTowMVrOAAZRcw%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QxODozMDoyN1rOAAY_5Q%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yM1QxODoyOTowNVrOAAfz8g%3D%3D
NVD CVE-2026-110871: https://nvd.nist.gov/vuln/detail/CVE-2026-110871
NVD CVE API 2.0: https://services.nvd.nist.gov/rest/json/cves/2.0?pubStartDate=2026-10-12T13:35:55.142Z&pubEndDate=2026-10-26T13:35:55.142Z&resultsPerPage=500&startIndex=3500
FIRST measured EPSS score: https://api.first.org/data/v1/epss?cve=CVE-2026-110871&date=2026-10-26
Record ID: CVE-2026-110871.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Oct 27, 2026, 01:40 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are not available yet. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail