{"id":"CVE-2026-110870","aliases":["CLSA-2026-1793180903","CLSA-2026-1793183905","CLSA-2026-1793187944","CLSA-2026-1793207112","CLSA-2026-1793209433","CLSA-2026-1793215120","CLSA-2026-1793267771","CLSA-2026-1793446166","CLSA-2026-1794409427","DEBIAN-CVE-2026-110870","EUVD-2026-101753","GHSA-597p-2mmq-hhg9","GHSA-g73g-hqqh-jr95","UBUNTU-CVE-2026-110870"],"status":"published","title":"Nodemailer before 10.0.13 Improper Input Validation via Angle-Bracket Address Comments","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00266,"percentile":0.18946,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-20","CWE-436"],"published":"2026-10-14T22:52:58Z","modified":"2026-11-11T19:30:04Z","fixes":[{"ecosystem":"npm","package":"nodemailer","fixed_in":["10.0.13"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"nodemailer","fixed_in":["9.1.0-tuxcare.8","9.0.6-tuxcare.7","9.0.5-tuxcare.8","9.0.3-tuxcare.7","9.0.2-tuxcare.8","9.0.1-tuxcare.6","9.0.0-tuxcare.8"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"node-nodemailer","fixed_in":["10.0.13+~8.0.2-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.0"},{"last_affected":"10.0.12"},{"fixed":"10.0.13"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.1.0-tuxcare.0"},{"fixed":"9.1.0-tuxcare.8"}]}],"versions":["9.1.0","9.1.0-tuxcare.1","9.1.0-tuxcare.2","9.1.0-tuxcare.3","9.1.0-tuxcare.4","9.1.0-tuxcare.5","9.1.0-tuxcare.6","9.1.0-tuxcare.7"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.6-tuxcare.0"},{"fixed":"9.0.6-tuxcare.7"}]}],"versions":["9.0.6","9.0.6-tuxcare.1","9.0.6-tuxcare.2","9.0.6-tuxcare.3","9.0.6-tuxcare.4","9.0.6-tuxcare.5","9.0.6-tuxcare.6"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.5-tuxcare.0"},{"fixed":"9.0.5-tuxcare.8"}]}],"versions":["9.0.5","9.0.5-tuxcare.1","9.0.5-tuxcare.2","9.0.5-tuxcare.3","9.0.5-tuxcare.4","9.0.5-tuxcare.5","9.0.5-tuxcare.6","9.0.5-tuxcare.7"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.3-tuxcare.0"},{"fixed":"9.0.3-tuxcare.7"}]}],"versions":["9.0.3","9.0.3-tuxcare.1","9.0.3-tuxcare.2","9.0.3-tuxcare.3","9.0.3-tuxcare.4","9.0.3-tuxcare.5","9.0.3-tuxcare.6"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.2-tuxcare.0"},{"fixed":"9.0.2-tuxcare.8"}]}],"versions":["9.0.2","9.0.2-tuxcare.1","9.0.2-tuxcare.2","9.0.2-tuxcare.3","9.0.2-tuxcare.4","9.0.2-tuxcare.5","9.0.2-tuxcare.6","9.0.2-tuxcare.7"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.1-tuxcare.0"},{"fixed":"9.0.1-tuxcare.6"}]}],"versions":["9.0.1","9.0.1-tuxcare.1","9.0.1-tuxcare.2","9.0.1-tuxcare.3","9.0.1-tuxcare.4","9.0.1-tuxcare.5"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.0-tuxcare.0"},{"fixed":"9.0.0-tuxcare.8"}]}],"versions":["9.0.0","9.0.0-tuxcare.1","9.0.0-tuxcare.2","9.0.0-tuxcare.3","9.0.0-tuxcare.4","9.0.0-tuxcare.5","9.0.0-tuxcare.6","9.0.0-tuxcare.7"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"6.10.1-tuxcare.0"},{"fixed":"6.10.1-tuxcare.17"}]}],"versions":["6.10.1","6.10.1-tuxcare.1","6.10.1-tuxcare.10","6.10.1-tuxcare.11","6.10.1-tuxcare.12","6.10.1-tuxcare.13","6.10.1-tuxcare.14","6.10.1-tuxcare.15","6.10.1-tuxcare.16","6.10.1-tuxcare.2","6.10.1-tuxcare.3","6.10.1-tuxcare.4","6.10.1-tuxcare.5","6.10.1-tuxcare.6","6.10.1-tuxcare.7","6.10.1-tuxcare.8","6.10.1-tuxcare.9"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"7.0.13-tuxcare.0"},{"fixed":"7.0.13-tuxcare.1"}]}],"versions":["7.0.13"],"bundled":7},{"source":"osv","ecosystem":"Debian:12","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["10.0.0+~8.0.1-1","10.0.10+~8.0.1-1","10.0.10+~8.0.2-1","10.0.13+~8.0.2-1","10.0.14+~8.0.2-1","10.1.7+~8.0.2-1","6.10.0+~6.4.17-1","6.8.0+~6.4.6-1","6.9.13+~6.4.14-1","6.9.15+~6.4.16-1","6.9.16+~6.4.16-1","6.9.4+~6.4.9-1","6.9.4+~6.4.9-2","6.9.4+~6.4.9-3","6.9.4+~6.4.9-4","7.0.10+~7.0.2-1","7.0.12+~7.0.5-1","7.0.5+~7.0.1-1","7.0.6+~7.0.1-1","7.0.9+~7.0.2-1","7.0.9+~7.0.2-2","8.0.11+~8.0.1-1","8.0.2+~7.0.11-1","8.0.3+~7.0.11-1","8.0.4+~7.0.11-1","8.0.4+~7.0.11-2","9.0.0+~8.0.1-1","9.0.3+~8.0.1-1","9.0.5+~8.0.1-1","9.0.6+~8.0.1-1"]},{"source":"osv","ecosystem":"Debian:13","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["10.0.0+~8.0.1-1","10.0.10+~8.0.1-1","10.0.10+~8.0.2-1","10.0.13+~8.0.2-1","10.0.14+~8.0.2-1","10.1.7+~8.0.2-1","6.10.0+~6.4.17-1","6.10.0+~6.4.17-1+deb13u1","7.0.10+~7.0.2-1","7.0.12+~7.0.5-1","7.0.5+~7.0.1-1","7.0.6+~7.0.1-1","7.0.9+~7.0.2-1","7.0.9+~7.0.2-2","8.0.11+~8.0.1-1","8.0.2+~7.0.11-1","8.0.3+~7.0.11-1","8.0.4+~7.0.11-1","8.0.4+~7.0.11-2","9.0.0+~8.0.1-1","9.0.3+~8.0.1-1","9.0.5+~8.0.1-1","9.0.6+~8.0.1-1"]},{"source":"osv","ecosystem":"Debian:14","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"10.0.13+~8.0.2-1"}]}],"versions":["10.0.0+~8.0.1-1","10.0.10+~8.0.1-1","10.0.10+~8.0.2-1","6.10.0+~6.4.17-1","7.0.10+~7.0.2-1","7.0.12+~7.0.5-1","7.0.5+~7.0.1-1","7.0.6+~7.0.1-1","7.0.9+~7.0.2-1","7.0.9+~7.0.2-2","8.0.11+~8.0.1-1","8.0.2+~7.0.11-1","8.0.3+~7.0.11-1","8.0.4+~7.0.11-1","8.0.4+~7.0.11-2","9.0.0+~8.0.1-1","9.0.3+~8.0.1-1","9.0.5+~8.0.1-1","9.0.6+~8.0.1-1"]},{"source":"osv","ecosystem":"npm","package":"nodemailer","ranges":[{"type":"SEMVER","events":[{"introduced":"3.0.0"},{"fixed":"10.0.13"}]}]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["6.3.0-2","6.3.1-1","6.4.0-1","6.4.1-1","6.4.2-1","6.4.5-1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["6.4.17-3","6.7.0-1","6.7.0-2","6.7.1+~6.4.4-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["6.9.4+~6.4.9-2","6.9.4+~6.4.9-3ubuntu1","6.9.4+~6.4.9-4"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["8.0.4+~7.0.11-1","8.0.4+~7.0.11-2","9.0.0+~8.0.1-1","9.0.3+~8.0.1-1","9.0.5+~8.0.1-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"node-nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["7.0.5+~7.0.1-1","7.0.9+~7.0.2-2","7.0.10+~7.0.2-1","7.0.12+~7.0.5-1","8.0.4+~7.0.11-1"]}],"products":[{"vendor":"nodemailer","product":"nodemailer"}],"references":[{"url":"https://github.com/nodemailer/nodemailer/security/advisories/GHSA-g73g-hqqh-jr95","tags":["vendor-advisory","exploit","advisory","web","report"]},{"url":"https://www.vulncheck.com/advisories/nodemailer-before-10.0.13-improper-input-validation-via-angle-bracket-address-comments","tags":["third-party-advisory","advisory","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-110870","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-597p-2mmq-hhg9"},{"url":"https://github.com/nodemailer/nodemailer/commit/a502247555fd81773eac337486781f3b6008e16a","tags":["web"]},{"url":"https://github.com/nodemailer/nodemailer/releases/tag/v10.0.13","tags":["web"]},{"url":"https://github.com/advisories/GHSA-g73g-hqqh-jr95"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/110xxx/CVE-2026-110870.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-110870","tags":["advisory"]},{"url":"https://github.com/nodemailer/nodemailer","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-110870","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-110870","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-110870","title":"Nodemailer before 10.0.13 Improper Input Validation via Angle-Bracket Address Comments","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T22:52:58.787Z","modified":"2026-10-26T15:20:53.532Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T13:34:26.24368Z"}],"euvd":[{"id":"EUVD-2026-101753","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T22:52:58Z","modified":"2026-10-26T15:20:53Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T16:59:29.561971Z"}],"ghsa":[{"id":"GHSA-597p-2mmq-hhg9","title":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its...","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-15T00:31:19Z","modified":"2026-10-15T00:31:26Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/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","level":"medium"},{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:13:29.914076Z"},{"id":"GHSA-g73g-hqqh-jr95","title":"Nodemailer: RFC 5322 comment inside an angle-bracket address is never stripped","description":"## Summary\n\nNodemailer's address parser has three published, patched fixes for RFC 5322 comment `( ... )` handling inside an addr-spec: GHSA-cc9r-2j5m-2m83 (comment glued two domain atoms into one), GHSA-g57g-f23g-4646 (the same glue for a quoted local part), and the related ReDoS fixes GHSA-prgh-xp8r-p3m5 / GHSA-v53p-9fqp-m79j. All three fixes live in `_handleAddress()`'s `joins` / `_recoverAddrSpec()` logic in `src/addressparser/index.ts`, and that logic is exercised whenever the local text-accumulation path in the tokenizer runs.\n\nThat logic is **never reached at all** for the single most common way a recipient is written: a display name followed by an angle-bracket address, `Name \u003clocal@domain\u003e`. When the input inside the angle brackets itself contains an RFC 5322 comment, e.g.\n\n```\nLegit User \u003cuser@good-corp.com(x)evil.com\u003e\n```\n\nthe comment is not recognized as an operator, is not stripped, and the parenthesis characters survive **verbatim** inside the parsed address. That value is used as-is for `envelope.to` and is written raw onto the wire in the `RCPT TO` SMTP command. I confirmed this end to end:\n\n* against nodemailer's own `jsonTransport`/`streamTransport` (`envelope.to` and the emitted `To:` header both carry the malformed value), and\n* against a real SMTP server (`aiosmtpd`), where the malformed value is fatal to the transaction (`501 Syntax: RCPT TO: \u003caddress\u003e [SP \u003cmail-parameters\u003e]`, all recipients rejected).\n\nThis reproduces unmodified on every version I tested from `6.9.16` through the current `10.0.12` HEAD, i.e. it predates and survives all three of the address-parser advisories above unchanged, because it is a different root cause in a code path those fixes never touch.\n\n## Root cause\n\n### 1. The tokenizer has a single-slot operator stack\n\n`src/addressparser/index.ts`, `Tokenizer.checkChar()`:\n\n```ts\n} else if (!this.operatorExpecting \u0026\u0026 !this.inDomainLiteral \u0026\u0026 chr in this.operators) {\n    this.node = { type: 'operator', value: chr };\n    this.list.push(this.node);\n    this.node = null;\n    this.operatorExpecting = this.operators[chr];\n    this.escaped = false;\n    return;\n}\n```\n(currently around line 670)\n\n`operatorExpecting` is a single string, not a stack. Once `\u003c` opens the address scope, `operatorExpecting` is set to `'\u003e'`. The branch above that recognizes a *new* operator character (such as `(`) is gated on `!this.operatorExpecting`, which is now false. So while inside `\u003c...\u003e`, a `(` is never tokenized as an operator at all — it is just an ordinary character appended to the running text token, together with everything up to the next `\u003e`. The RFC 5322 comment is not recognized, not entered, and not stripped: it rides straight through as literal text.\n\n### 2. The `_handleAddress` state machine loses the `address` state on any operator boundary\n\n`src/addressparser/index.ts`, inside `_handleAddress()` (currently around line 365-388):\n\n```ts\nswitch (token.value) {\n    case '\u003c':\n        state = 'address';\n        insideQuotes = false;\n        break;\n    case '(':\n        state = 'comment';\n        insideQuotes = false;\n        break;\n    ...\n    default:\n        state = 'text';\n        insideQuotes = false;\n        break;\n}\n```\n\nEven where a comment *is* recognized (outside angle brackets), closing it with `)` falls through to `default` and resets `state` to `'text'`, not back to whatever state was active before the comment opened. This is why the fixed cases (bare address, quoted local part) only work at all: the leaked comment tail lands in the free-`text` bucket, where `_recoverAddrSpec()`/the ADDR_SPEC extraction logic can find and strip it. Inside `\u003c...\u003e`, none of that applies in the first place (point 1), so there is no text-bucket safety net to fall back on — the whole point 1/point 2 interaction is what makes the angle-bracket case categorically different from, and unprotected by, the three existing fixes.\n\n### 3. The one existing safety net requires whitespace, and this value has none\n\n`_recoverAddrSpec()` (currently around line 283) is the function all three prior fixes rely on to peel a clean addr-spec back out of a polluted value. Its very first line is:\n\n```ts\nfunction _recoverAddrSpec(data: { address: string; text: string }): void {\n    if (!HAS_WHITESPACE.test(data.address)) {\n        return;\n    }\n    ...\n```\n\n`user@good-corp.com(x)evil.com` contains no whitespace at all — the comment content `x` had none, and nothing in the angle-bracket path inserts any — so `_recoverAddrSpec` bails out immediately and never even looks at the value. There is no cleanup path for this shape.\n\n## Proof of Concept\n\nEnvironment: Node.js, current `master` (commit `30e4cfd5902fd5da4d1bc76dc8e4bc92d288adef`, package.json version `10.0.12`), no special transport configuration required.\n\n### Parser-level confirmation\n\n```js\nconst addressparser = require('nodemailer/lib/addressparser');\nconsole.log(addressparser('Legit User \u003cuser@good-corp.com(x)evil.com\u003e'));\n// [ { address: 'user@good-corp.com(x)evil.com', name: 'Legit User' } ]\n```\n\nCompare with the already-fixed, unbracketed form of the exact same bytes:\n\n```js\nconsole.log(addressparser('user@good-corp.com(x)evil.com'));\n// [ { address: 'user@good-corp.com', name: 'evil.com' } ]   \u003c- correctly stripped\n```\n\n### `sendMail` / envelope confirmation (jsonTransport, no network)\n\n```js\nconst nodemailer = require('nodemailer');\n\n(async () =\u003e {\n    const t = nodemailer.createTransport({ jsonTransport: true });\n    const info = await t.sendMail({\n        from: 'app@good-corp.com',\n        to: 'Legit User \u003cuser@good-corp.com(x)evil.com\u003e',\n        subject: 'hi',\n        text: 'x'\n    });\n    console.log('envelope.to:', info.envelope.to);\n})();\n```\n\nObserved output:\n\n```\nenvelope.to: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nThe raw `streamTransport` message shows the same value in the emitted header:\n\n```\nFrom: app@good-corp.com\nTo: Legit User \u003cuser@good-corp.com(x)evil.com\u003e\nSubject: hi\n```\n\n### Wire-level confirmation against a real SMTP server\n\n```js\nconst net = require('net');\nconst nodemailer = require('nodemailer');\n\nconst server = net.createServer(sock =\u003e {\n    let buf = '';\n    sock.write('220 sink ESMTP\\r\\n');\n    sock.on('data', d =\u003e {\n        buf += d;\n        let i;\n        while ((i = buf.indexOf('\\r\\n')) \u003e= 0) {\n            const line = buf.slice(0, i);\n            buf = buf.slice(i + 2);\n            const u = line.toUpperCase();\n            if (u.startsWith('EHLO')) sock.write('250-sink\\r\\n250 8BITMIME\\r\\n');\n            else if (u.startsWith('MAIL')) sock.write('250 ok\\r\\n');\n            else if (u.startsWith('RCPT')) { console.log('wire RCPT TO :', line); sock.write('250 ok\\r\\n'); }\n            else if (u.startsWith('DATA')) sock.write('354 go\\r\\n');\n            else if (line === '.') sock.write('250 ok\\r\\n');\n            else if (u.startsWith('QUIT')) { sock.write('221 bye\\r\\n'); sock.end(); }\n            else sock.write('250 ok\\r\\n');\n        }\n    });\n});\n\nserver.listen(0, '127.0.0.1', async () =\u003e {\n    const t = nodemailer.createTransport({ host: '127.0.0.1', port: server.address().port, secure: false });\n    const info = await t.sendMail({ from: 'app@good-corp.com', to: 'Legit User \u003cuser@good-corp.com(x)evil.com\u003e', subject: 'hi', text: 'x' });\n    console.log('envelope.to:', info.envelope.to);\n    t.close(); server.close();\n});\n```\n\nObserved:\n\n```\nwire RCPT TO : RCPT TO:\u003cuser@good-corp.com(x)evil.com\u003e\nenvelope.to: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nThe same recipient string sent at a real, conformant SMTP server (`aiosmtpd`, a widely used reference-grade Python SMTP implementation) instead of the sink above:\n\n```\nError: Can't send mail - all recipients were rejected: 501 Syntax: RCPT TO: \u003caddress\u003e [SP \u003cmail-parameters\u003e]\ncode: 'EENVELOPE', responseCode: 501, command: 'RCPT TO',\nrejected: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nSo against a strict receiver the immediate, concretely demonstrated outcome is that the message is bounced: an ordinary-looking recipient address that a sender/application believes is valid (and, see below, that some app-side validators *do* read as valid, at `good-corp.com`) silently fails to send.\n\n### Interpretation-conflict confirmation (same class as GHSA-cc9r-2j5m-2m83)\n\nThe same string, read by two other parsers an application might use to gate on the recipient's domain, produces two more, mutually different readings:\n\n```python\n\u003e\u003e\u003e import email.utils\n\u003e\u003e\u003e email.utils.parseaddr('Legit User \u003cuser@good-corp.com(x)evil.com\u003e')\n('Legit User (x)', 'user@good-corp.comevil.com')\n```\n\n```python\n\u003e\u003e\u003e import email.policy, email.parser\n\u003e\u003e\u003e msg = email.parser.Parser(policy=email.policy.default).parsestr(\n...     'To: Legit User \u003cuser@good-corp.com(x)evil.com\u003e\\n\\n')\n\u003e\u003e\u003e addr = msg['To'].addresses[0]\n\u003e\u003e\u003e addr.username, addr.domain\n('user', 'good-corp.com')\n```\n\nSo for the exact same bytes:\n\n| Parser | Domain read | \n|---|---|\n| Python `email.utils.parseaddr` | `good-corp.comevil.com` (attacker-registrable) |\n| Python `email.policy.default` (defects not inspected) | `good-corp.com` (clean, would pass an allow-list check) |\n| Nodemailer `addressparser` (this report) | `good-corp.com(x)evil.com` (illegal characters, rejected by a strict MTA) |\n\nThis is the exact \"which parser sees what\" comparison table from GHSA-cc9r-2j5m-2m83, reproduced through a different, unpatched code path: an application validating with `email.policy.default` without checking `.defects` (a documented pattern in that advisory) approves `good-corp.com` and hands the string to Nodemailer, which then emits a different, malformed value on the wire.\n\n### Version range\n\nThe built `addressparser`/`sendMail` output was tested for this exact input across published releases:\n\n| Version | `envelope.to` |\n|---|---|\n| `6.9.16` | `user@good-corp.com(x)evil.com` |\n| `9.0.6` | `user@good-corp.com(x)evil.com` |\n| `9.1.0` (fixed GHSA-cc9r-2j5m-2m83) | `user@good-corp.com(x)evil.com` |\n| `10.0.1` | `user@good-corp.com(x)evil.com` |\n| `10.0.12` / current HEAD (fixed GHSA-g57g-f23g-4646) | `user@good-corp.com(x)evil.com` |\n\nUnchanged across every one of the three address-parser advisory fixes, because none of them touch the tokenizer's single-slot `operatorExpecting` state or the `_handleAddress` state-machine reset on `)`.\n\n## Impact\n\n* **Confirmed, demonstrated impact:** a recipient address that contains a perfectly legal RFC 5322 comment inside angle brackets — the single most common way a `To`/`Cc`/`Bcc`/`From`/`Reply-To` value is written (`Display Name \u003cuser@domain\u003e`) — is parsed into a value containing SMTP-illegal characters (`(`, `)`) that a conformant receiving SMTP server rejects outright, silently failing message delivery to what the sender/application believed was a valid, already-validated recipient (CWE-20, availability/reliability impact on outbound mail).\n* **Demonstrated interpretation conflict (CWE-436):** the malformed value that reaches `envelope.to` and the wire `RCPT TO` command disagrees with how other parsers, including ones explicitly called out as an at-risk validation pattern in GHSA-cc9r-2j5m-2m83 (Python's `email.policy.default` without defect inspection), read the exact same bytes. An application gating outbound mail on \"is the recipient's domain in my allow-list\" using such a validator can approve a value that Nodemailer then sends corrupted, and depending on the receiving MTA's own comment handling, a different domain than either the sender or the validator believed was being used.\n* **Reachability:** every code path that ends up calling `MimeNode._parseAddresses()` — `to`, `cc`, `bcc`, `from`, `replyTo`, and the explicit `envelope` option — is affected identically, since they all funnel through the same `addressparser()`/`_handleAddress()` logic.\n\n## Suggested fix\n\nThe underlying issue is architectural: `Tokenizer.operatorExpecting` is a single value, so it cannot represent \"currently inside an angle-address, and also currently inside a comment nested within it.\" A minimal fix is to make the tokenizer maintain a stack of expected closing characters (pushing `')'` when entering a comment while another operator scope, e.g. `'\u003e'`, is already open, and popping back to the outer expectation on `)`), so a comment can be recognized and stripped no matter what scope it is nested inside. `_handleAddress`'s state machine would then need to restore the previous `state` on a comment close instead of unconditionally resetting to `'text'`, so recognized-but-stripped comment content inside `\u003c...\u003e` continues to accumulate as `address` rather than leaking into `text`.\n\nAs defense in depth, `_recoverAddrSpec()` (or an equivalent check run specifically on `data.address` for the angle-bracket case) should also reject/clean any parsed address value that still contains RFC 5322 special characters such as `(`/`)` regardless of whether whitespace is present, since `envelope.to` is used directly to build an SMTP command and any character outside `Mailbox` (RFC 5321 §4.1.2) reaching that string is already the wrong outcome, independent of how it got there.\n\n## Regression test suggestion\n\n`Name \u003cuser@example.com(x)evil.com\u003e` and the bcc/cc/from/replyTo/envelope equivalents should all parse to `{ address: 'user@example.com', name: 'Name evil.com' }` or be rejected outright by `sendMail`, never silently carry `(`/`)` into `envelope.to`.","status":"published","published":"2026-10-27T15:04:36Z","modified":"2026-10-27T15:04:36Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"}],"cwes":["CWE-20","CWE-436"],"updated_at":"2026-11-02T14:41:37.912288Z"}],"nvd":[{"id":"CVE-2026-110870","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T23:17:28.813Z","modified":"2026-10-26T16:17:11.983Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/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","level":"medium","assessor":"secondary"},{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:12:59.328869Z"}],"osv":[{"id":"CVE-2026-110870","title":"Nodemailer before 10.0.13 Improper Input Validation via Angle-Bracket Address Comments","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T22:52:58.787Z","modified":"2026-10-28T02:30:28.765756022Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:44:55.61515Z"},{"id":"DEBIAN-CVE-2026-110870","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T23:17:28.813Z","modified":"2026-10-27T10:00:40.559378984Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/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","level":"medium"}],"updated_at":"2026-11-02T15:45:49.824149Z"},{"id":"GHSA-g73g-hqqh-jr95","title":"Nodemailer: RFC 5322 comment inside an angle-bracket address is never stripped","description":"## Summary\n\nNodemailer's address parser has three published, patched fixes for RFC 5322 comment `( ... )` handling inside an addr-spec: GHSA-cc9r-2j5m-2m83 (comment glued two domain atoms into one), GHSA-g57g-f23g-4646 (the same glue for a quoted local part), and the related ReDoS fixes GHSA-prgh-xp8r-p3m5 / GHSA-v53p-9fqp-m79j. All three fixes live in `_handleAddress()`'s `joins` / `_recoverAddrSpec()` logic in `src/addressparser/index.ts`, and that logic is exercised whenever the local text-accumulation path in the tokenizer runs.\n\nThat logic is **never reached at all** for the single most common way a recipient is written: a display name followed by an angle-bracket address, `Name \u003clocal@domain\u003e`. When the input inside the angle brackets itself contains an RFC 5322 comment, e.g.\n\n```\nLegit User \u003cuser@good-corp.com(x)evil.com\u003e\n```\n\nthe comment is not recognized as an operator, is not stripped, and the parenthesis characters survive **verbatim** inside the parsed address. That value is used as-is for `envelope.to` and is written raw onto the wire in the `RCPT TO` SMTP command. I confirmed this end to end:\n\n* against nodemailer's own `jsonTransport`/`streamTransport` (`envelope.to` and the emitted `To:` header both carry the malformed value), and\n* against a real SMTP server (`aiosmtpd`), where the malformed value is fatal to the transaction (`501 Syntax: RCPT TO: \u003caddress\u003e [SP \u003cmail-parameters\u003e]`, all recipients rejected).\n\nThis reproduces unmodified on every version I tested from `6.9.16` through the current `10.0.12` HEAD, i.e. it predates and survives all three of the address-parser advisories above unchanged, because it is a different root cause in a code path those fixes never touch.\n\n## Root cause\n\n### 1. The tokenizer has a single-slot operator stack\n\n`src/addressparser/index.ts`, `Tokenizer.checkChar()`:\n\n```ts\n} else if (!this.operatorExpecting \u0026\u0026 !this.inDomainLiteral \u0026\u0026 chr in this.operators) {\n    this.node = { type: 'operator', value: chr };\n    this.list.push(this.node);\n    this.node = null;\n    this.operatorExpecting = this.operators[chr];\n    this.escaped = false;\n    return;\n}\n```\n(currently around line 670)\n\n`operatorExpecting` is a single string, not a stack. Once `\u003c` opens the address scope, `operatorExpecting` is set to `'\u003e'`. The branch above that recognizes a *new* operator character (such as `(`) is gated on `!this.operatorExpecting`, which is now false. So while inside `\u003c...\u003e`, a `(` is never tokenized as an operator at all — it is just an ordinary character appended to the running text token, together with everything up to the next `\u003e`. The RFC 5322 comment is not recognized, not entered, and not stripped: it rides straight through as literal text.\n\n### 2. The `_handleAddress` state machine loses the `address` state on any operator boundary\n\n`src/addressparser/index.ts`, inside `_handleAddress()` (currently around line 365-388):\n\n```ts\nswitch (token.value) {\n    case '\u003c':\n        state = 'address';\n        insideQuotes = false;\n        break;\n    case '(':\n        state = 'comment';\n        insideQuotes = false;\n        break;\n    ...\n    default:\n        state = 'text';\n        insideQuotes = false;\n        break;\n}\n```\n\nEven where a comment *is* recognized (outside angle brackets), closing it with `)` falls through to `default` and resets `state` to `'text'`, not back to whatever state was active before the comment opened. This is why the fixed cases (bare address, quoted local part) only work at all: the leaked comment tail lands in the free-`text` bucket, where `_recoverAddrSpec()`/the ADDR_SPEC extraction logic can find and strip it. Inside `\u003c...\u003e`, none of that applies in the first place (point 1), so there is no text-bucket safety net to fall back on — the whole point 1/point 2 interaction is what makes the angle-bracket case categorically different from, and unprotected by, the three existing fixes.\n\n### 3. The one existing safety net requires whitespace, and this value has none\n\n`_recoverAddrSpec()` (currently around line 283) is the function all three prior fixes rely on to peel a clean addr-spec back out of a polluted value. Its very first line is:\n\n```ts\nfunction _recoverAddrSpec(data: { address: string; text: string }): void {\n    if (!HAS_WHITESPACE.test(data.address)) {\n        return;\n    }\n    ...\n```\n\n`user@good-corp.com(x)evil.com` contains no whitespace at all — the comment content `x` had none, and nothing in the angle-bracket path inserts any — so `_recoverAddrSpec` bails out immediately and never even looks at the value. There is no cleanup path for this shape.\n\n## Proof of Concept\n\nEnvironment: Node.js, current `master` (commit `30e4cfd5902fd5da4d1bc76dc8e4bc92d288adef`, package.json version `10.0.12`), no special transport configuration required.\n\n### Parser-level confirmation\n\n```js\nconst addressparser = require('nodemailer/lib/addressparser');\nconsole.log(addressparser('Legit User \u003cuser@good-corp.com(x)evil.com\u003e'));\n// [ { address: 'user@good-corp.com(x)evil.com', name: 'Legit User' } ]\n```\n\nCompare with the already-fixed, unbracketed form of the exact same bytes:\n\n```js\nconsole.log(addressparser('user@good-corp.com(x)evil.com'));\n// [ { address: 'user@good-corp.com', name: 'evil.com' } ]   \u003c- correctly stripped\n```\n\n### `sendMail` / envelope confirmation (jsonTransport, no network)\n\n```js\nconst nodemailer = require('nodemailer');\n\n(async () =\u003e {\n    const t = nodemailer.createTransport({ jsonTransport: true });\n    const info = await t.sendMail({\n        from: 'app@good-corp.com',\n        to: 'Legit User \u003cuser@good-corp.com(x)evil.com\u003e',\n        subject: 'hi',\n        text: 'x'\n    });\n    console.log('envelope.to:', info.envelope.to);\n})();\n```\n\nObserved output:\n\n```\nenvelope.to: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nThe raw `streamTransport` message shows the same value in the emitted header:\n\n```\nFrom: app@good-corp.com\nTo: Legit User \u003cuser@good-corp.com(x)evil.com\u003e\nSubject: hi\n```\n\n### Wire-level confirmation against a real SMTP server\n\n```js\nconst net = require('net');\nconst nodemailer = require('nodemailer');\n\nconst server = net.createServer(sock =\u003e {\n    let buf = '';\n    sock.write('220 sink ESMTP\\r\\n');\n    sock.on('data', d =\u003e {\n        buf += d;\n        let i;\n        while ((i = buf.indexOf('\\r\\n')) \u003e= 0) {\n            const line = buf.slice(0, i);\n            buf = buf.slice(i + 2);\n            const u = line.toUpperCase();\n            if (u.startsWith('EHLO')) sock.write('250-sink\\r\\n250 8BITMIME\\r\\n');\n            else if (u.startsWith('MAIL')) sock.write('250 ok\\r\\n');\n            else if (u.startsWith('RCPT')) { console.log('wire RCPT TO :', line); sock.write('250 ok\\r\\n'); }\n            else if (u.startsWith('DATA')) sock.write('354 go\\r\\n');\n            else if (line === '.') sock.write('250 ok\\r\\n');\n            else if (u.startsWith('QUIT')) { sock.write('221 bye\\r\\n'); sock.end(); }\n            else sock.write('250 ok\\r\\n');\n        }\n    });\n});\n\nserver.listen(0, '127.0.0.1', async () =\u003e {\n    const t = nodemailer.createTransport({ host: '127.0.0.1', port: server.address().port, secure: false });\n    const info = await t.sendMail({ from: 'app@good-corp.com', to: 'Legit User \u003cuser@good-corp.com(x)evil.com\u003e', subject: 'hi', text: 'x' });\n    console.log('envelope.to:', info.envelope.to);\n    t.close(); server.close();\n});\n```\n\nObserved:\n\n```\nwire RCPT TO : RCPT TO:\u003cuser@good-corp.com(x)evil.com\u003e\nenvelope.to: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nThe same recipient string sent at a real, conformant SMTP server (`aiosmtpd`, a widely used reference-grade Python SMTP implementation) instead of the sink above:\n\n```\nError: Can't send mail - all recipients were rejected: 501 Syntax: RCPT TO: \u003caddress\u003e [SP \u003cmail-parameters\u003e]\ncode: 'EENVELOPE', responseCode: 501, command: 'RCPT TO',\nrejected: [ 'user@good-corp.com(x)evil.com' ]\n```\n\nSo against a strict receiver the immediate, concretely demonstrated outcome is that the message is bounced: an ordinary-looking recipient address that a sender/application believes is valid (and, see below, that some app-side validators *do* read as valid, at `good-corp.com`) silently fails to send.\n\n### Interpretation-conflict confirmation (same class as GHSA-cc9r-2j5m-2m83)\n\nThe same string, read by two other parsers an application might use to gate on the recipient's domain, produces two more, mutually different readings:\n\n```python\n\u003e\u003e\u003e import email.utils\n\u003e\u003e\u003e email.utils.parseaddr('Legit User \u003cuser@good-corp.com(x)evil.com\u003e')\n('Legit User (x)', 'user@good-corp.comevil.com')\n```\n\n```python\n\u003e\u003e\u003e import email.policy, email.parser\n\u003e\u003e\u003e msg = email.parser.Parser(policy=email.policy.default).parsestr(\n...     'To: Legit User \u003cuser@good-corp.com(x)evil.com\u003e\\n\\n')\n\u003e\u003e\u003e addr = msg['To'].addresses[0]\n\u003e\u003e\u003e addr.username, addr.domain\n('user', 'good-corp.com')\n```\n\nSo for the exact same bytes:\n\n| Parser | Domain read | \n|---|---|\n| Python `email.utils.parseaddr` | `good-corp.comevil.com` (attacker-registrable) |\n| Python `email.policy.default` (defects not inspected) | `good-corp.com` (clean, would pass an allow-list check) |\n| Nodemailer `addressparser` (this report) | `good-corp.com(x)evil.com` (illegal characters, rejected by a strict MTA) |\n\nThis is the exact \"which parser sees what\" comparison table from GHSA-cc9r-2j5m-2m83, reproduced through a different, unpatched code path: an application validating with `email.policy.default` without checking `.defects` (a documented pattern in that advisory) approves `good-corp.com` and hands the string to Nodemailer, which then emits a different, malformed value on the wire.\n\n### Version range\n\nThe built `addressparser`/`sendMail` output was tested for this exact input across published releases:\n\n| Version | `envelope.to` |\n|---|---|\n| `6.9.16` | `user@good-corp.com(x)evil.com` |\n| `9.0.6` | `user@good-corp.com(x)evil.com` |\n| `9.1.0` (fixed GHSA-cc9r-2j5m-2m83) | `user@good-corp.com(x)evil.com` |\n| `10.0.1` | `user@good-corp.com(x)evil.com` |\n| `10.0.12` / current HEAD (fixed GHSA-g57g-f23g-4646) | `user@good-corp.com(x)evil.com` |\n\nUnchanged across every one of the three address-parser advisory fixes, because none of them touch the tokenizer's single-slot `operatorExpecting` state or the `_handleAddress` state-machine reset on `)`.\n\n## Impact\n\n* **Confirmed, demonstrated impact:** a recipient address that contains a perfectly legal RFC 5322 comment inside angle brackets — the single most common way a `To`/`Cc`/`Bcc`/`From`/`Reply-To` value is written (`Display Name \u003cuser@domain\u003e`) — is parsed into a value containing SMTP-illegal characters (`(`, `)`) that a conformant receiving SMTP server rejects outright, silently failing message delivery to what the sender/application believed was a valid, already-validated recipient (CWE-20, availability/reliability impact on outbound mail).\n* **Demonstrated interpretation conflict (CWE-436):** the malformed value that reaches `envelope.to` and the wire `RCPT TO` command disagrees with how other parsers, including ones explicitly called out as an at-risk validation pattern in GHSA-cc9r-2j5m-2m83 (Python's `email.policy.default` without defect inspection), read the exact same bytes. An application gating outbound mail on \"is the recipient's domain in my allow-list\" using such a validator can approve a value that Nodemailer then sends corrupted, and depending on the receiving MTA's own comment handling, a different domain than either the sender or the validator believed was being used.\n* **Reachability:** every code path that ends up calling `MimeNode._parseAddresses()` — `to`, `cc`, `bcc`, `from`, `replyTo`, and the explicit `envelope` option — is affected identically, since they all funnel through the same `addressparser()`/`_handleAddress()` logic.\n\n## Suggested fix\n\nThe underlying issue is architectural: `Tokenizer.operatorExpecting` is a single value, so it cannot represent \"currently inside an angle-address, and also currently inside a comment nested within it.\" A minimal fix is to make the tokenizer maintain a stack of expected closing characters (pushing `')'` when entering a comment while another operator scope, e.g. `'\u003e'`, is already open, and popping back to the outer expectation on `)`), so a comment can be recognized and stripped no matter what scope it is nested inside. `_handleAddress`'s state machine would then need to restore the previous `state` on a comment close instead of unconditionally resetting to `'text'`, so recognized-but-stripped comment content inside `\u003c...\u003e` continues to accumulate as `address` rather than leaking into `text`.\n\nAs defense in depth, `_recoverAddrSpec()` (or an equivalent check run specifically on `data.address` for the angle-bracket case) should also reject/clean any parsed address value that still contains RFC 5322 special characters such as `(`/`)` regardless of whether whitespace is present, since `envelope.to` is used directly to build an SMTP command and any character outside `Mailbox` (RFC 5321 §4.1.2) reaching that string is already the wrong outcome, independent of how it got there.\n\n## Regression test suggestion\n\n`Name \u003cuser@example.com(x)evil.com\u003e` and the bcc/cc/from/replyTo/envelope equivalents should all parse to `{ address: 'user@example.com', name: 'Name evil.com' }` or be rejected outright by `sendMail`, never silently carry `(`/`)` into `envelope.to`.","status":"published","published":"2026-10-27T15:04:36Z","modified":"2026-10-27T15:15:07.723338698Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"}],"cwes":["CWE-20","CWE-436"],"updated_at":"2026-11-02T15:46:47.472172Z"},{"id":"UBUNTU-CVE-2026-110870","description":"Nodemailer from 3.0.0 before 10.0.13 contains an improper input validation vulnerability in its address parser that fails to strip RFC 5322 comments inside angle-bracket addresses. Attackers can supply recipients like a display name with a parenthesized comment in the domain, so illegal characters reach envelope.to and RCPT TO, causing rejected delivery or parser interpretation conflicts that bypass domain allow-lists.","status":"published","published":"2026-10-14T23:17:00Z","modified":"2026-10-28T17:50:34Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"},{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T16:55:59.591851Z"},{"id":"CLSA-2026-1793180903","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.1.0-tuxcare.8.","status":"published","published":"2026-10-28T09:48:23Z","modified":"2026-10-28T13:45:17.239364269Z","updated_at":"2026-11-02T15:43:55.34846Z"},{"id":"CLSA-2026-1793183905","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.0.6-tuxcare.7.","status":"published","published":"2026-10-28T10:38:25Z","modified":"2026-10-28T13:45:02.688788599Z","updated_at":"2026-11-02T15:43:55.34846Z"},{"id":"CLSA-2026-1793187944","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.0.5-tuxcare.8.","status":"published","published":"2026-10-28T11:45:44Z","modified":"2026-10-28T13:45:17.778841866Z","updated_at":"2026-11-02T15:43:55.34846Z"},{"id":"CLSA-2026-1793207112","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.0.3-tuxcare.7.","status":"published","published":"2026-10-28T17:05:12Z","modified":"2026-10-28T19:45:12.954085649Z","updated_at":"2026-11-02T15:43:55.669514Z"},{"id":"CLSA-2026-1793209433","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.0.2-tuxcare.8.","status":"published","published":"2026-10-28T17:43:53Z","modified":"2026-10-28T19:45:12.994654151Z","updated_at":"2026-11-02T15:43:55.669514Z"},{"id":"CLSA-2026-1793215120","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 9.0.1-tuxcare.6.","status":"published","published":"2026-10-28T19:18:40Z","modified":"2026-11-07T19:13:15.474324497Z","updated_at":"2026-11-07T19:21:34.633914Z"},{"id":"CLSA-2026-1793267771","title":"TuxCare security update for nodemailer (1 CVE)","description":"TuxCare rebuilt nodemailer to address 1 CVE. Fixed in 9.0.0-tuxcare.8.","status":"published","published":"2026-10-29T09:56:11Z","modified":"2026-10-29T13:45:02.565422403Z","updated_at":"2026-11-02T15:43:55.669514Z"},{"id":"CLSA-2026-1793446166","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 6.10.1-tuxcare.17.","status":"published","published":"2026-10-31T12:29:27Z","modified":"2026-10-31T14:45:02.241378881Z","updated_at":"2026-11-02T15:43:55.669514Z"},{"id":"CLSA-2026-1794409427","title":"TuxCare security update for nodemailer (7 CVEs)","description":"TuxCare rebuilt nodemailer to address 7 CVEs. Fixed in 7.0.13-tuxcare.1.","status":"published","published":"2026-11-11T16:03:48Z","modified":"2026-11-11T19:30:04.686106232Z","updated_at":"2026-11-11T19:50:24.684142Z"}]}}
