Bug 1526456 - junk after address should not be parsed as the address. r=kaie
authorMagnus Melin <mkmelin+mozilla@iki.fi>
Thu, 13 Feb 2020 13:37:17 +0200
changeset 37341 cd29db7dc1055c7b6db7d571cb3b49c0ffe5e0be
parent 37340 bebaf03305400717e81ea5a71004c485b988eaba
child 37342 b55b6873476a54a0712ddff905bd45e52ffbcf35
push id2566
push userclokep@gmail.com
push dateMon, 09 Mar 2020 19:20:31 +0000
treeherdercomm-beta@a352facfa0a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskaie
bugs1526456
Bug 1526456 - junk after address should not be parsed as the address. r=kaie
mailnews/mime/jsmime/jsmime.js
mailnews/mime/jsmime/test/unit/test_header.js
mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
--- a/mailnews/mime/jsmime/jsmime.js
+++ b/mailnews/mime/jsmime/jsmime.js
@@ -892,17 +892,18 @@
       let name = "",
         groupName = "",
         localPart = "",
         address = "",
         comment = "";
       // Indicators of current state
       let inAngle = false,
         inComment = false,
-        needsSpace = false;
+        needsSpace = false,
+        afterAddress = false;
       let preserveSpace = false;
       let commentClosed = false;
 
       // RFC 5322 ยง3.4 notes that legacy implementations exist which use a simple
       // recipient form where the addr-spec appears without the angle brackets,
       // but includes the name of the recipient in parentheses as a comment
       // following the addr-spec. While we do not create this format, we still
       // want to recognize it, though.
@@ -952,17 +953,17 @@
             lastComment.length - offset - 1
           );
         }
         if (displayName !== "" || addrSpec !== "") {
           addrlist.push({ name: displayName, email: addrSpec });
         }
         // Clear pending flags and variables.
         name = localPart = address = lastComment = "";
-        inAngle = inComment = needsSpace = false;
+        inAngle = inComment = needsSpace = afterAddress = false;
       }
 
       // Main parsing loop
       for (let token of getHeaderTokens(header, ":,;<>@", {
         qstring: true,
         comments: true,
         dliteral: true,
         rfc2047: doRFC2047,
@@ -971,30 +972,31 @@
           groupName = name;
           name = "";
           localPart = "";
           // If we had prior email address results, commit them to the top-level.
           if (addrlist.length > 0) {
             results = results.concat(addrlist);
           }
           addrlist = [];
-        } else if (token === "<") {
+        } else if (token === "<" && !afterAddress) {
           if (inAngle) {
             // Interpret the address we were parsing as a name.
             if (address.length > 0) {
               name = address;
             }
             localPart = address = "";
           } else {
             inAngle = true;
           }
-        } else if (token === ">") {
+        } else if (token === ">" && !afterAddress) {
           inAngle = false;
           // Forget addr-spec comments.
           lastComment = "";
+          afterAddress = true;
         } else if (token === "(") {
           inComment = true;
           // The needsSpace flag may not always be set even if it should be,
           // e.g. for a comment behind an angle-addr.
           // Also, we need to restore the needsSpace flag if we ignore the comment.
           preserveSpace = needsSpace;
           if (!needsSpace) {
             needsSpace = name !== "" && name.substr(-1) !== " ";
@@ -1011,32 +1013,36 @@
             needsSpace = preserveSpace;
           } else {
             name += comment;
             needsSpace = true;
           }
           commentClosed = true;
           continue;
         } else if (token === "@") {
+          if (afterAddress) {
+            continue;
+          }
           // An @ means we see an email address. If we're not within <> brackets,
           // then we just parsed an email address instead of a display name. Empty
           // out the display name for the current production.
           if (!inAngle) {
             address = localPart;
             name = "";
             localPart = "";
             // The remainder of this mailbox is part of an addr-spec.
             inAngle = true;
           }
           address += "@";
         } else if (token === ",") {
           // A comma ends the current name. If we have something that's kind of a
           // name, add it to the result list. If we don't, then our input looks like
           // To: , , -> don't bother adding an empty entry.
           addToAddrList(name, address);
+          afterAddress = false;
         } else if (token === ";") {
           // Add pending name to the list
           addToAddrList(name, address);
 
           // If no group name was found, treat the ';' as a ','. In any case, we
           // need to copy the results of addrlist into either a new group object or
           // the main list.
           if (groupName === "") {
@@ -1065,17 +1071,19 @@
           }
 
           // Which field do we add this data to?
           if (inComment) {
             comment += spacedToken;
           } else if (inAngle) {
             address += spacedToken;
           } else {
-            name += spacedToken;
+            if (!afterAddress) {
+              name += spacedToken;
+            }
             // Never add a space to the local-part, if we just ignored a comment.
             if (commentClosed) {
               localPart += token;
               commentClosed = false;
             } else {
               localPart += spacedToken;
             }
           }
--- a/mailnews/mime/jsmime/test/unit/test_header.js
+++ b/mailnews/mime/jsmime/test/unit/test_header.js
@@ -680,16 +680,34 @@ define(function(require) {
           "Foe \u00A0\u00A0\u2003 A <foe@example.com>",
           [{ name: "Foe A", email: "foe@example.com" }],
         ],
         // Remove tabs.
         [
           "Tabby \t \t A\t\tB <tab@example.com>",
           [{ name: "Tabby A B", email: "tab@example.com" }],
         ],
+        // Ensure it's the address in angles that is parsed as address.
+        [
+          "<attacker@example.com>friend@example.com\t, bystander@example.com,Someone (some@invalid) <someone@example.com>,",
+          [
+            { name: "", email: "attacker@example.com" },
+            { name: "", email: "bystander@example.com" },
+            { name: "Someone (some@invalid)", email: "someone@example.com" },
+          ],
+        ],
+        [
+          "me (via foo@example.com) <attacker2@example.com>friend2@example.com ",
+          [
+            {
+              name: "me (via foo@example.com)",
+              email: "attacker2@example.com",
+            },
+          ],
+        ],
       ];
       header_tests.forEach(function(data) {
         arrayTest(data, function() {
           assert.deepEqual(
             headerparser.parseAddressingHeader(data[0], false),
             data[1]
           );
         });
--- a/mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
+++ b/mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
@@ -103,16 +103,25 @@ function run_test() {
         '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"],
       ],
     },
+    {
+      displayString: "<attacker@example.com>friend@example.com",
+      addresses: [["", "attacker@example.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
     );