[Pkg-javascript-devel] Bug#1120717: trixie-pu: package node-nodemailer/6.10.0+~6.4.17-1+deb13u1

Yadd yadd at debian.org
Sat Nov 15 07:39:21 GMT 2025


Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: node-nodemailer at packages.debian.org, yadd at debian.org
Control: affects -1 + src:node-nodemailer
User: release.debian.org at packages.debian.org
Usertags: pu

[ Reason ]
node-nodemailer is vulnerable due to improper handling of specially
formatted recipient email addresses.

[ Impact ]
Medium vulnerability

[ Tests ]
Patch contains new tests

[ Risks ]
Low risk, test coverage is good and patch isn't complex

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
Better address parsing

Cheers,
Xavier
-------------- next part --------------
diff --git a/debian/changelog b/debian/changelog
index ba226b5..b4fb060 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+node-nodemailer (6.10.0+~6.4.17-1+deb13u1) trixie; urgency=medium
+
+  * Fix  addressparser handling of quoted nested email addresses
+    (Closes: CVE-2025-13033)
+
+ -- Yadd <yadd at debian.org>  Sat, 15 Nov 2025 08:34:41 +0100
+
 node-nodemailer (6.10.0+~6.4.17-1) unstable; urgency=medium
 
   * New upstream version 6.10.0+~6.4.17
diff --git a/debian/patches/CVE-2025-13033.patch b/debian/patches/CVE-2025-13033.patch
new file mode 100644
index 0000000..687b657
--- /dev/null
+++ b/debian/patches/CVE-2025-13033.patch
@@ -0,0 +1,141 @@
+Description: Fix addressparser handling of quoted nested email addresses
+Author: Andris Reinman <andris at reinman.eu>
+Origin: upstream, https://github.com/nodemailer/nodemailer/commit/1150d99f
+Bug: https://github.com/nodemailer/nodemailer/security/advisories/GHSA-mm7p-fcc7-pg87
+Forwarded: not-needed
+Applied-Upstream: 7.0.7, commit:1150d99f
+Reviewed-By: Xavier Guimard <yadd at debian.org>
+Last-Update: 2025-11-15
+
+--- a/lib/addressparser/index.js
++++ b/lib/addressparser/index.js
+@@ -15,10 +15,12 @@
+         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 @@
+             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 @@
+             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 @@
+         // 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 at 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 @@
+             // 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;
++                        }
+                     }
+                 }
+             }
+--- a/test/addressparser/addressparser-test.js
++++ b/test/addressparser/addressparser-test.js
+@@ -309,4 +309,40 @@
+         ];
+         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 at gmail.com x"@internal.domain';
++        let result = addressparser(input);
++        // Should preserve full address, NOT extract xclow3n at gmail.com
++        assert.strictEqual(result.length, 1);
++        assert.strictEqual(result[0].address.includes('@internal.domain'), true);
++        assert.strictEqual(result[0].address, 'xclow3n at gmail.com x at internal.domain');
++    });
++
++    it('should handle quoted local-part with attacker domain (security)', () => {
++        let input = '"user at 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 at attacker.com@legitimate.com');
++    });
++
++    it('should handle multiple @ in quoted local-part (security)', () => {
++        let input = '"a at b@c"@example.com';
++        let result = addressparser(input);
++        // Should not extract a at b or b at c
++        assert.strictEqual(result.length, 1);
++        assert.strictEqual(result[0].address, 'a at b@c at example.com');
++    });
++
++    it('should handle quoted local-part with angle brackets', () => {
++        let input = 'Name <"user at 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 at domain.com"@example.com');
++    });
+ });
diff --git a/debian/patches/series b/debian/patches/series
index 37d5831..40dc79a 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
 drop-timeout-based-test.patch
 #fix-test-for-proxy-2.patch
 change-test-ports.patch
+CVE-2025-13033.patch


More information about the Pkg-javascript-devel mailing list