Bug 1485105 - Allow 12-19 digit length card numbers. r=MattN
☠☠ backed out by 9605a2bb0c59 ☠ ☠
authorSam Foster <sfoster@mozilla.com>
Wed, 10 Oct 2018 23:23:11 +0000
changeset 440650 e702628b7c514789f8372ac593da4ea9b445b7e2
parent 440649 f9118ebe1bf160af3b6ddf2ffee8cdbd5b3ab5dc
child 440651 ba07c543cc935aed4a56a495a031dc8cd970290b
push id70740
push usersfoster@mozilla.com
push dateWed, 10 Oct 2018 23:24:21 +0000
treeherderautoland@e702628b7c51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1485105
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1485105 - Allow 12-19 digit length card numbers. r=MattN Differential Revision: https://phabricator.services.mozilla.com/D8271
toolkit/modules/CreditCard.jsm
toolkit/modules/tests/xpcshell/test_CreditCard.js
--- a/toolkit/modules/CreditCard.jsm
+++ b/toolkit/modules/CreditCard.jsm
@@ -103,44 +103,46 @@ class CreditCard {
   get number() {
     return this._number;
   }
 
   set number(value) {
     if (value) {
       let normalizedNumber = value.replace(/[-\s]/g, "");
       // Based on the information on wiki[1], the shortest valid length should be
-      // 9 digits (Canadian SIN).
-      // [1] https://en.wikipedia.org/wiki/Social_Insurance_Number
-      normalizedNumber = normalizedNumber.match(/^\d{9,}$/) ?
+      // 12 digits (Maestro).
+      // [1] https://en.wikipedia.org/wiki/Payment_card_number
+      normalizedNumber = normalizedNumber.match(/^\d{12,}$/) ?
         normalizedNumber : null;
       this._number = normalizedNumber;
     }
   }
 
   get network() {
     return this._network;
   }
 
   set network(value) {
     this._network = value || undefined;
   }
 
   // Implements the Luhn checksum algorithm as described at
   // http://wikipedia.org/wiki/Luhn_algorithm
+  // Number digit lengths vary with network, but should fall within 12-19 range. [2]
+  // More details at https://en.wikipedia.org/wiki/Payment_card_number
   isValidNumber() {
     if (!this._number) {
       return false;
     }
 
     // Remove dashes and whitespace
     let number = this._number.replace(/[\-\s]/g, "");
 
     let len = number.length;
-    if (len != 9 && len != 15 && len != 16) {
+    if (len < 12 || len > 19) {
       return false;
     }
 
     if (!/^\d+$/.test(number)) {
       return false;
     }
 
     let total = 0;
--- a/toolkit/modules/tests/xpcshell/test_CreditCard.js
+++ b/toolkit/modules/tests/xpcshell/test_CreditCard.js
@@ -10,30 +10,52 @@ add_task(function isValidNumber() {
     if (shouldPass) {
       ok(CreditCard.isValidNumber(number), `${number} should be considered valid`);
     } else {
       ok(!CreditCard.isValidNumber(number), `${number} should not be considered valid`);
     }
   }
 
   testValid("0000000000000000", true);
+
+  testValid("41111111112", false); // passes Luhn but too short
+  testValid("4111-1111-112", false); // passes Luhn but too short
+  testValid("55555555555544440018", false); // passes Luhn but too long
+  testValid("5555 5555 5555 4444 0018", false); // passes Luhn but too long
+
   testValid("4929001587121045", true);
   testValid("5103059495477870", true);
   testValid("6011029476355493", true);
   testValid("3589993783099582", true);
   testValid("5415425865751454", true);
-  if (CreditCard.isValidNumber("30190729470495")) {
-    ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
-  }
-  if (CreditCard.isValidNumber("36333851788250")) {
-    ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
-  }
-  if (CreditCard.isValidNumber("3532596776688495393")) {
-    ok(false, "todo: 19-digit numbers (JCB, Discover, Maestro) could have 16-19 digits");
-  }
+
+  testValid("378282246310005", true); // American Express test number
+  testValid("371449635398431", true); // American Express test number
+  testValid("378734493671000", true); // American Express Corporate test number
+  testValid("5610591081018250", true); // Australian BankCard test number
+  testValid("6759649826438453", true); // Maestro test number
+  testValid("6799990100000000019", true); // 19 digit Maestro test number
+  testValid("6799-9901-0000-0000019", true); // 19 digit Maestro test number
+  testValid("30569309025904", true); // 14 digit Diners Club test number
+  testValid("38520000023237", true); // 14 digit Diners Club test number
+  testValid("6011111111111117", true); // Discover test number
+  testValid("6011000990139424", true); // Discover test number
+  testValid("3530111333300000", true); // JCB test number
+  testValid("3566002020360505", true); // JCB test number
+  testValid("3532596776688495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits
+  testValid("3532 5967 7668 8495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits
+  testValid("5555555555554444", true); // MasterCard test number
+  testValid("5105105105105100", true); // MasterCard test number
+  testValid("2221000000000009", true); // 2-series MasterCard test number
+  testValid("4111111111111111", true); // Visa test number
+  testValid("4012888888881881", true); // Visa test number
+  testValid("4222222222222", true); // 13 digit Visa test number
+  testValid("4222 2222 22222", true); // 13 digit Visa test number
+  testValid("4035 5010 0000 0008", true); // Visadebit/Cartebancaire test number
+
   testValid("5038146897157463", true);
   testValid("4026313395502338", true);
   testValid("6387060366272981", true);
   testValid("474915027480942", true);
   testValid("924894781317325", true);
   testValid("714816113937185", true);
   testValid("790466087343106", true);
   testValid("474320195408363", true);
@@ -50,17 +72,18 @@ add_task(function isValidNumber() {
   testValid("4302068493801686", true);
   testValid("2721398408985465", true);
   testValid("6160334316984331", true);
   testValid("8643619970075142", true);
   testValid("0218246069710785", true);
   testValid("0000-0000-0080-4609", true);
   testValid("0000 0000 0222 331", true);
   testValid("344060747836806", true);
-  testValid("001064088", true);
+  testValid("001064088", false); // too short
+  testValid("00-10-64-088", false); // still too short
   testValid("4929001587121046", false);
   testValid("5103059495477876", false);
   testValid("6011029476355494", false);
   testValid("3589993783099581", false);
   testValid("5415425865751455", false);
   testValid("5038146897157462", false);
   testValid("4026313395502336", false);
   testValid("6387060366272980", false);
@@ -112,16 +135,17 @@ add_task(function test_maskNumber() {
   }
   testMask("0000000000000000", "**** 0000");
   testMask("4929001587121045", "**** 1045");
   testMask("5103059495477870", "**** 7870");
   testMask("6011029476355493", "**** 5493");
   testMask("3589993783099582", "**** 9582");
   testMask("5415425865751454", "**** 1454");
   testMask("344060747836806", "**** 6806");
+  testMask("6799990100000000019", "**** 0019");
   Assert.throws(() => (new CreditCard({number: "1234"})).maskedNumber,
     /Invalid credit card number/,
     "Four or less numbers should throw when retrieving the maskedNumber");
 });
 
 add_task(function test_longMaskedNumber() {
   function testMask(number, expected) {
     let card = new CreditCard({number});
@@ -130,16 +154,18 @@ add_task(function test_longMaskedNumber(
   }
   testMask("0000000000000000", "************0000");
   testMask("4929001587121045", "************1045");
   testMask("5103059495477870", "************7870");
   testMask("6011029476355493", "************5493");
   testMask("3589993783099582", "************9582");
   testMask("5415425865751454", "************1454");
   testMask("344060747836806", "***********6806");
+  testMask("6799990100000000019", "***************0019");
+
   Assert.throws(() => (new CreditCard({number: "1234"})).longMaskedNumber,
     /Invalid credit card number/,
     "Four or less numbers should throw when retrieving the maskedNumber");
 });
 
 add_task(function test_isValid() {
   function testValid(number, expirationMonth, expirationYear, shouldPass, message) {
     let card = new CreditCard({