test_Chrome_credit_cards.js (7246B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* global structuredClone */ 7 8 const PROFILE = { 9 id: "Default", 10 name: "Default", 11 }; 12 13 const PAYMENT_METHODS = [ 14 { 15 name_on_card: "Name Name", 16 card_number: "4532962432748929", // Visa 17 expiration_month: 3, 18 expiration_year: 2027, 19 }, 20 { 21 name_on_card: "Name Name Name", 22 card_number: "5359908373796416", // Mastercard 23 expiration_month: 5, 24 expiration_year: 2028, 25 }, 26 { 27 name_on_card: "Name", 28 card_number: "346624461807588", // AMEX 29 expiration_month: 4, 30 expiration_year: 2026, 31 }, 32 ]; 33 34 let OSKeyStoreTestUtils; 35 add_setup(async function os_key_store_setup() { 36 ({ OSKeyStoreTestUtils } = ChromeUtils.importESModule( 37 "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" 38 )); 39 OSKeyStoreTestUtils.setup(); 40 registerCleanupFunction(async function cleanup() { 41 await OSKeyStoreTestUtils.cleanup(); 42 }); 43 }); 44 45 let rootDir = do_get_file("chromefiles/", true); 46 47 function checkCardsAreEqual(importedCard, testCard, id) { 48 const CC_NUMBER_RE = /^(\*+)(.{4})$/; 49 50 Assert.equal( 51 importedCard["cc-name"], 52 testCard.name_on_card, 53 "The two logins ID " + id + " have the same name on card" 54 ); 55 56 let matches = CC_NUMBER_RE.exec(importedCard["cc-number"]); 57 Assert.notEqual(matches, null); 58 Assert.equal(importedCard["cc-number"].length, testCard.card_number.length); 59 Assert.equal(testCard.card_number.endsWith(matches[2]), true); 60 Assert.notEqual(importedCard["cc-number-encrypted"], ""); 61 62 Assert.equal( 63 importedCard["cc-exp-month"], 64 testCard.expiration_month, 65 "The two logins ID " + id + " have the same expiration month" 66 ); 67 Assert.equal( 68 importedCard["cc-exp-year"], 69 testCard.expiration_year, 70 "The two logins ID " + id + " have the same expiration year" 71 ); 72 } 73 74 add_task(async function setup_fakePaths() { 75 let pathId; 76 if (AppConstants.platform == "macosx") { 77 pathId = "ULibDir"; 78 } else if (AppConstants.platform == "win") { 79 pathId = "LocalAppData"; 80 } else { 81 pathId = "Home"; 82 } 83 registerFakePath(pathId, rootDir); 84 }); 85 86 add_task(async function test_credit_cards() { 87 if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { 88 todo_check_true( 89 OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), 90 "Cannot test OS key store login on official builds." 91 ); 92 return; 93 } 94 95 let loginCrypto; 96 let profilePathSegments; 97 98 let mockMacOSKeychain = { 99 passphrase: "bW96aWxsYWZpcmVmb3g=", 100 serviceName: "TESTING Chrome Safe Storage", 101 accountName: "TESTING Chrome", 102 }; 103 if (AppConstants.platform == "macosx") { 104 let { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule( 105 "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs" 106 ); 107 loginCrypto = new ChromeMacOSLoginCrypto( 108 mockMacOSKeychain.serviceName, 109 mockMacOSKeychain.accountName, 110 mockMacOSKeychain.passphrase 111 ); 112 profilePathSegments = [ 113 "Application Support", 114 "Google", 115 "Chrome", 116 "Default", 117 ]; 118 } else if (AppConstants.platform == "win") { 119 // We no longer support importing payment methods from Chrome on Windows, 120 // but we can still do it for some other Chrome-based browsers like Canary. 121 let { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule( 122 "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs" 123 ); 124 loginCrypto = new ChromeWindowsLoginCrypto("Chrome Beta"); 125 profilePathSegments = ["Google", "Chrome Beta", "User Data", "Default"]; 126 } else { 127 throw new Error("Not implemented"); 128 } 129 130 let target = rootDir.clone(); 131 let defaultFolderPath = PathUtils.join(target.path, ...profilePathSegments); 132 let webDataPath = PathUtils.join(defaultFolderPath, "Web Data"); 133 let localStatePath = defaultFolderPath.replace("Default", ""); 134 135 await IOUtils.makeDirectory(defaultFolderPath, { 136 createAncestor: true, 137 ignoreExisting: true, 138 }); 139 140 // Copy Web Data database into Default profile 141 const sourcePathWebData = do_get_file( 142 "AppData/Local/Google/Chrome/User Data/Default/Web Data" 143 ).path; 144 await IOUtils.copy(sourcePathWebData, webDataPath); 145 146 const sourcePathLocalState = do_get_file( 147 "AppData/Local/Google/Chrome/User Data/Local State" 148 ).path; 149 await IOUtils.copy(sourcePathLocalState, localStatePath); 150 151 let dbConn = await Sqlite.openConnection({ path: webDataPath }); 152 153 for (let card of PAYMENT_METHODS) { 154 let encryptedCardNumber = await loginCrypto.encryptData(card.card_number); 155 let cardNumberEncryptedValue = new Uint8Array( 156 loginCrypto.stringToArray(encryptedCardNumber) 157 ); 158 159 let cardCopy = structuredClone(card); 160 161 cardCopy.card_number_encrypted = cardNumberEncryptedValue; 162 delete cardCopy.card_number; 163 164 await dbConn.execute( 165 `INSERT INTO credit_cards 166 (name_on_card, card_number_encrypted, expiration_month, expiration_year) 167 VALUES (:name_on_card, :card_number_encrypted, :expiration_month, :expiration_year) 168 `, 169 cardCopy 170 ); 171 } 172 173 await dbConn.close(); 174 175 // We no longer support importing payment methods from Chrome on Windows, 176 // but we can still do it for other Chrome-based browsers like Chrome Beta. 177 let migratorKey = AppConstants.platform == "win" ? "chrome-beta" : "chrome"; 178 let migrator = await MigrationUtils.getMigrator(migratorKey); 179 180 if (AppConstants.platform == "macosx") { 181 Object.assign(migrator, { 182 _keychainServiceName: mockMacOSKeychain.serviceName, 183 _keychainAccountName: mockMacOSKeychain.accountName, 184 _keychainMockPassphrase: mockMacOSKeychain.passphrase, 185 }); 186 } 187 Assert.ok( 188 await migrator.isSourceAvailable(), 189 "Sanity check the source exists" 190 ); 191 192 Services.prefs.setBoolPref( 193 "browser.migrate.chrome.payment_methods.enabled", 194 false 195 ); 196 Assert.ok( 197 !( 198 (await migrator.getMigrateData(PROFILE)) & 199 MigrationUtils.resourceTypes.PAYMENT_METHODS 200 ), 201 "Should be able to disable migrating payment methods" 202 ); 203 // Clear the cached resources now so that a re-check for payment methods 204 // will look again. 205 delete migrator._resourcesByProfile[PROFILE.id]; 206 207 Services.prefs.setBoolPref( 208 "browser.migrate.chrome.payment_methods.enabled", 209 true 210 ); 211 212 Assert.ok( 213 (await migrator.getMigrateData(PROFILE)) & 214 MigrationUtils.resourceTypes.PAYMENT_METHODS, 215 "Should be able to enable migrating payment methods" 216 ); 217 218 let { formAutofillStorage } = ChromeUtils.importESModule( 219 "resource://autofill/FormAutofillStorage.sys.mjs" 220 ); 221 await formAutofillStorage.initialize(); 222 223 await promiseMigration( 224 migrator, 225 MigrationUtils.resourceTypes.PAYMENT_METHODS, 226 PROFILE 227 ); 228 229 let cards = await formAutofillStorage.creditCards.getAll(); 230 231 Assert.equal( 232 cards.length, 233 PAYMENT_METHODS.length, 234 "Check there are still the same number of credit cards after re-importing the data" 235 ); 236 Assert.equal( 237 cards.length, 238 MigrationUtils._importQuantities.cards, 239 "Check telemetry matches the actual import." 240 ); 241 242 for (let i = 0; i < PAYMENT_METHODS.length; i++) { 243 checkCardsAreEqual(cards[i], PAYMENT_METHODS[i], i + 1); 244 } 245 });