Bug 1526456 - handle additional cases of junk after email addresses. r=pmorris DONTBUILD
authorMagnus Melin <mkmelin+mozilla@iki.fi>
Mon, 06 Apr 2020 16:37:53 +0300
changeset 38693 426213bc6cb981a05aef4281fe2f0cea555864c5
parent 38692 d10f3b74984e91ef82ffa87a332dac641ea3b9bd
child 38694 011b96fed1e5d9d0c5a4ba043f07ba979e1aadda
push id400
push userclokep@gmail.com
push dateMon, 04 May 2020 18:56:09 +0000
reviewerspmorris
bugs1526456
Bug 1526456 - handle additional cases of junk after email addresses. r=pmorris DONTBUILD
mailnews/mime/jsmime/test/unit/test_header.js
mailnews/mime/src/MimeJSComponents.jsm
mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
--- a/mailnews/mime/jsmime/test/unit/test_header.js
+++ b/mailnews/mime/jsmime/test/unit/test_header.js
@@ -704,16 +704,38 @@ define(function(require) {
           "me (via foo@example.com) <attacker2@example.com>friend2@example.com ",
           [
             {
               name: "me (via foo@example.com)",
               email: "attacker2@example.com",
             },
           ],
         ],
+        [
+          "<attacker@example.com> <friend@example.com>,friend2@example.com<attacker2@example.com>",
+          [
+            {
+              name: "",
+              email: "attacker@example.com",
+            },
+            {
+              name: "friend2@example.com",
+              email: "attacker2@example.com",
+            },
+          ],
+        ],
+        [
+          'My "XX ><<friend2@example.com>" YY <attacker@example.com> <friend@example.com> ("mr ><x")',
+          [
+            {
+              name: "My XX ><<friend2@example.com> YY (mr ><x)",
+              email: "attacker@example.com",
+            },
+          ],
+        ],
       ];
       header_tests.forEach(function(data) {
         arrayTest(data, function() {
           assert.deepEqual(
             headerparser.parseAddressingHeader(data[0], false),
             data[1]
           );
         });
--- a/mailnews/mime/src/MimeJSComponents.jsm
+++ b/mailnews/mime/src/MimeJSComponents.jsm
@@ -423,27 +423,47 @@ MimeAddressParser.prototype = {
       addr = addr.trimLeft();
       if (addr) {
         output.push(this._makeSingleAddress(addr));
       }
     }
     return output;
   },
 
-  // Construct a single email address from a name <local@domain> token.
-  _makeSingleAddress(aDisplayName) {
-    if (aDisplayName.includes("<")) {
-      let lbracket = aDisplayName.lastIndexOf("<");
-      let rbracket = aDisplayName.lastIndexOf(">");
+  /**
+   * Construct a single email address from an |name <local@domain>| token.
+   * @param {string} aInput - a string to be parsed to a mailbox object.
+   * @returns {msgIAddressObject} the mailbox parsed from the input.
+   */
+  _makeSingleAddress(aInput) {
+    // If the whole string is within quotes, unquote it first.
+    aInput = aInput.trim().replace(/^"(.*)"$/, "$1");
+
+    if (aInput.includes("<")) {
+      // We don't want to look for the address within quotes.
+      let cleanedInput = aInput.replace(/".+"/g, "").trim();
+      if (!cleanedInput) {
+        // all quoted
+        return this.makeMailboxObject(aInput, "");
+      }
+      if (!/<.+>/.test(cleanedInput)) {
+        // no proper address
+        return this.makeMailboxObject(aInput, "");
+      }
+      let addr = cleanedInput.slice(
+        cleanedInput.indexOf("<") + 1,
+        cleanedInput.indexOf(">")
+      );
+      let addrIdx = aInput.indexOf("<" + addr + ">");
       return this.makeMailboxObject(
-        lbracket == 0 ? "" : aDisplayName.slice(0, lbracket).trim(),
-        aDisplayName.slice(lbracket + 1, rbracket)
+        addrIdx == 0 ? "" : aInput.slice(0, addrIdx).trim(),
+        addr
       );
     }
-    return this.makeMailboxObject("", aDisplayName);
+    return this.makeMailboxObject("", aInput);
   },
 
   extractHeaderAddressMailboxes(aLine) {
     return this.parseDecodedHeader(aLine)
       .map(addr => addr.email)
       .join(", ");
   },
 
--- a/mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
+++ b/mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
@@ -89,50 +89,65 @@ function run_test() {
       addresses: [['Yatter "XXX" King4', "a@a.a.a"]],
     },
     {
       displayString: '"Yatter "XXX" King5" <a@a.a.a>',
       addresses: [['"Yatter "XXX" King5"', "a@a.a.a"]],
     },
     {
       displayString: '"Yatter King6 <a@a.a.a>"',
-      addresses: [['"Yatter King6', "a@a.a.a"]],
+      addresses: [["Yatter King6", "a@a.a.a"]],
     },
     {
       displayString: '"Yatter King7 <a@a.a.a>" <b@b.b.b>',
       addresses: [['"Yatter King7 <a@a.a.a>"', "b@b.b.b"]],
     },
     // Handle invalid mailbox separation with semicolons gracefully.
     {
       displayString:
         'Bart <bart@example.com> ; lisa@example.com;  "Homer, J; President" <pres@example.com>, Marge <marge@example.com>; ',
       addresses: [
         ["Bart", "bart@example.com"],
         ["", "lisa@example.com"],
         ['"Homer, J; President"', "pres@example.com"],
         ["Marge", "marge@example.com"],
       ],
     },
+    // Junk after a bracketed email address to be ignored.
     {
       displayString: "<attacker@example.com>friend@example.com",
       addresses: [["", "attacker@example.com"]],
     },
     {
       displayString:
+        "<attacker2@example.com><friend2@example.com>,foo <attacker3@example.com><friend3@example.com>",
+      addresses: [
+        ["", "attacker2@example.com"],
+        ["foo", "attacker3@example.com"],
+      ],
+    },
+    {
+      displayString:
+        'jay "bad" ass <name@evil.com> <someone-else@bad.com> <name@evil.commercial.org>',
+      addresses: [['jay "bad" ass', "name@evil.com"]],
+    },
+
+    {
+      displayString:
         'me "you" (via foo@example.com) <attacker2@example.com> friend@example.com,',
       addresses: [['me "you" (via foo@example.com)', "attacker2@example.com"]],
     },
   ];
 
   // Test -  strings
 
   for (let i = 0; i < checks.length; ++i) {
     let addrs = MailServices.headerParser.makeFromDisplayAddress(
       checks[i].displayString
     );
     let checkaddrs = checks[i].addresses;
-    Assert.equal(addrs.length, checkaddrs.length);
+    Assert.equal(addrs.length, checkaddrs.length, "Number of parsed addresses");
     for (let j = 0; j < addrs.length; j++) {
-      Assert.equal(addrs[j].name, checkaddrs[j][0]);
-      Assert.equal(addrs[j].email, checkaddrs[j][1]);
+      Assert.equal(addrs[j].name, checkaddrs[j][0], "Parsed name");
+      Assert.equal(addrs[j].email, checkaddrs[j][1], "Parsed email");
     }
   }
 }