CVE-2025-13033
Description
A vulnerability was identified in the email parsing library due to improper handling of specially formatted recipient email addresses. An attacker can exploit this flaw by crafting a recipient address that embeds an external address within quotes. This causes the application to misdirect the email to the attacker's external address instead of the intended internal recipient. This could lead to a significant data leak of sensitive information and allow an attacker to bypass security filters and access controls.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
nodemailernpm | < 7.0.7 | 7.0.7 |
Affected products
1- Range: v0.1, v0.1.1, v0.1.10, …
Patches
11150d99fba77fix(addressparser): Fixed addressparser handling of quoted nested email addresses
2 files changed · +66 −6
lib/addressparser/index.js+30 −6 modified@@ -15,10 +15,12 @@ function _handleAddress(tokens) { address: [], comment: [], group: [], - text: [] + text: [], + textWasQuoted: [] // Track which text tokens came from inside quotes }; let i; let len; + let insideQuotes = false; // Track if we're currently inside a quoted string // Filter out <addresses>, (comments) and regular text for (i = 0, len = tokens.length; i < len; i++) { @@ -28,16 +30,25 @@ function _handleAddress(tokens) { switch (token.value) { case '<': state = 'address'; + insideQuotes = false; break; case '(': state = 'comment'; + insideQuotes = false; break; case ':': state = 'group'; isGroup = true; + insideQuotes = false; + break; + case '"': + // Track quote state for text tokens + insideQuotes = !insideQuotes; + state = 'text'; break; default: state = 'text'; + insideQuotes = false; break; } } else if (token.value) { @@ -51,8 +62,14 @@ function _handleAddress(tokens) { if (prevToken && prevToken.noBreak && data[state].length) { // join values data[state][data[state].length - 1] += token.value; + if (state === 'text' && insideQuotes) { + data.textWasQuoted[data.textWasQuoted.length - 1] = true; + } } else { data[state].push(token.value); + if (state === 'text') { + data.textWasQuoted.push(insideQuotes); + } } } } @@ -74,8 +91,12 @@ function _handleAddress(tokens) { // If no address was found, try to detect one from regular text if (!data.address.length && data.text.length) { for (i = data.text.length - 1; i >= 0; i--) { - if (data.text[i].match(/^[^@\s]+@[^@\s]+$/)) { + // Security fix: Do not extract email addresses from quoted strings + // RFC 5321 allows @ inside quoted local-parts like "user@domain"@example.com + // Extracting emails from quoted text leads to misrouting vulnerabilities + if (!data.textWasQuoted[i] && data.text[i].match(/^[^@\s]+@[^@\s]+$/)) { data.address = data.text.splice(i, 1); + data.textWasQuoted.splice(i, 1); break; } } @@ -92,10 +113,13 @@ function _handleAddress(tokens) { // still no address if (!data.address.length) { for (i = data.text.length - 1; i >= 0; i--) { - // fixed the regex to parse email address correctly when email address has more than one @ - data.text[i] = data.text[i].replace(/\s*\b[^@\s]+@[^\s]+\b\s*/, _regexHandler).trim(); - if (data.address.length) { - break; + // Security fix: Do not extract email addresses from quoted strings + if (!data.textWasQuoted[i]) { + // fixed the regex to parse email address correctly when email address has more than one @ + data.text[i] = data.text[i].replace(/\s*\b[^@\s]+@[^\s]+\b\s*/, _regexHandler).trim(); + if (data.address.length) { + break; + } } } }
test/addressparser/addressparser-test.js+36 −0 modified@@ -309,4 +309,40 @@ describe('#addressparser', () => { ]; assert.deepStrictEqual(addressparser(input), expected); }); + + // Security tests for RFC 5321/5322 quoted local-part handling + it('should not extract email from quoted local-part (security)', () => { + let input = '"xclow3n@gmail.com x"@internal.domain'; + let result = addressparser(input); + // Should preserve full address, NOT extract xclow3n@gmail.com + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].address.includes('@internal.domain'), true); + assert.strictEqual(result[0].address, 'xclow3n@gmail.com x@internal.domain'); + }); + + it('should handle quoted local-part with attacker domain (security)', () => { + let input = '"user@attacker.com"@legitimate.com'; + let result = addressparser(input); + // Should route to legitimate.com, not attacker.com + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].address.includes('@legitimate.com'), true); + assert.strictEqual(result[0].address, 'user@attacker.com@legitimate.com'); + }); + + it('should handle multiple @ in quoted local-part (security)', () => { + let input = '"a@b@c"@example.com'; + let result = addressparser(input); + // Should not extract a@b or b@c + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].address, 'a@b@c@example.com'); + }); + + it('should handle quoted local-part with angle brackets', () => { + let input = 'Name <"user@domain.com"@example.com>'; + let result = addressparser(input); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].name, 'Name'); + // When address is in <>, quotes are preserved as part of the address + assert.strictEqual(result[0].address, '"user@domain.com"@example.com'); + }); });
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-mm7p-fcc7-pg87ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-13033ghsaADVISORY
- access.redhat.com/security/cve/CVE-2025-13033nvdWEB
- bugzilla.redhat.com/show_bug.cginvdWEB
- github.com/nodemailer/nodemailer/commit/1150d99fba77280df2cfb1885c43df23109a8626nvdWEB
- github.com/nodemailer/nodemailer/security/advisories/GHSA-mm7p-fcc7-pg87nvdWEB
- access.redhat.com/errata/RHSA-2026:15979nvd
- access.redhat.com/errata/RHSA-2026:3751nvd
News mentions
0No linked articles in our index yet.