diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/migration/tests/unit/test_Chrome_passwords.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | browser/components/migration/tests/unit/test_Chrome_passwords.js | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/browser/components/migration/tests/unit/test_Chrome_passwords.js b/browser/components/migration/tests/unit/test_Chrome_passwords.js new file mode 100644 index 0000000000..16f98e7038 --- /dev/null +++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js @@ -0,0 +1,373 @@ +"use strict"; + +const PROFILE = { + id: "Default", + name: "Person 1", +}; + +const TEST_LOGINS = [ + { + id: 1, // id of the row in the chrome login db + username: "username", + password: "password", + origin: "https://c9.io", + formActionOrigin: "https://c9.io", + httpRealm: null, + usernameField: "inputEmail", + passwordField: "inputPassword", + timeCreated: 1437418416037, + timePasswordChanged: 1437418416037, + timesUsed: 1, + }, + { + id: 2, + username: "username@gmail.com", + password: "password2", + origin: "https://accounts.google.com", + formActionOrigin: "https://accounts.google.com", + httpRealm: null, + usernameField: "Email", + passwordField: "Passwd", + timeCreated: 1437418446598, + timePasswordChanged: 1437418446598, + timesUsed: 6, + }, + { + id: 3, + username: "username", + password: "password3", + origin: "https://www.facebook.com", + formActionOrigin: "https://www.facebook.com", + httpRealm: null, + usernameField: "email", + passwordField: "pass", + timeCreated: 1437418478851, + timePasswordChanged: 1437418478851, + timesUsed: 1, + }, + { + id: 4, + username: "user", + password: "اقرأPÀßwörd", + origin: "http://httpbin.org", + formActionOrigin: null, + httpRealm: "me@kennethreitz.com", // Digest auth. + usernameField: "", + passwordField: "", + timeCreated: 1437787462368, + timePasswordChanged: 1437787462368, + timesUsed: 1, + }, + { + id: 5, + username: "buser", + password: "bpassword", + origin: "http://httpbin.org", + formActionOrigin: null, + httpRealm: "Fake Realm", // Basic auth. + usernameField: "", + passwordField: "", + timeCreated: 1437787539233, + timePasswordChanged: 1437787539233, + timesUsed: 1, + }, + { + id: 6, + username: "username", + password: "password6", + origin: "https://www.example.com", + formActionOrigin: "", // NULL `action_url` + httpRealm: null, + usernameField: "", + passwordField: "pass", + timeCreated: 1557291348878, + timePasswordChanged: 1557291348878, + timesUsed: 1, + }, + { + id: 7, + version: "v10", + username: "username", + password: "password", + origin: "https://v10.io", + formActionOrigin: "https://v10.io", + httpRealm: null, + usernameField: "inputEmail", + passwordField: "inputPassword", + timeCreated: 1437418416037, + timePasswordChanged: 1437418416037, + timesUsed: 1, + }, +]; + +var loginCrypto; +var dbConn; + +async function promiseSetPassword(login) { + const encryptedString = await loginCrypto.encryptData( + login.password, + login.version + ); + info(`promiseSetPassword: ${encryptedString}`); + const passwordValue = new Uint8Array( + loginCrypto.stringToArray(encryptedString) + ); + return dbConn.execute( + `UPDATE logins + SET password_value = :password_value + WHERE rowid = :rowid + `, + { password_value: passwordValue, rowid: login.id } + ); +} + +function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) { + passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo); + + Assert.equal( + passwordManagerLogin.username, + chromeLogin.username, + "The two logins ID " + id + " have the same username" + ); + Assert.equal( + passwordManagerLogin.password, + chromeLogin.password, + "The two logins ID " + id + " have the same password" + ); + Assert.equal( + passwordManagerLogin.origin, + chromeLogin.origin, + "The two logins ID " + id + " have the same origin" + ); + Assert.equal( + passwordManagerLogin.formActionOrigin, + chromeLogin.formActionOrigin, + "The two logins ID " + id + " have the same formActionOrigin" + ); + Assert.equal( + passwordManagerLogin.httpRealm, + chromeLogin.httpRealm, + "The two logins ID " + id + " have the same httpRealm" + ); + Assert.equal( + passwordManagerLogin.usernameField, + chromeLogin.usernameField, + "The two logins ID " + id + " have the same usernameElement" + ); + Assert.equal( + passwordManagerLogin.passwordField, + chromeLogin.passwordField, + "The two logins ID " + id + " have the same passwordElement" + ); + Assert.equal( + passwordManagerLogin.timeCreated, + chromeLogin.timeCreated, + "The two logins ID " + id + " have the same timeCreated" + ); + Assert.equal( + passwordManagerLogin.timePasswordChanged, + chromeLogin.timePasswordChanged, + "The two logins ID " + id + " have the same timePasswordChanged" + ); + Assert.equal( + passwordManagerLogin.timesUsed, + chromeLogin.timesUsed, + "The two logins ID " + id + " have the same timesUsed" + ); +} + +function generateDifferentLogin(login) { + const newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance( + Ci.nsILoginInfo + ); + + newLogin.init( + login.origin, + login.formActionOrigin, + null, + login.username, + login.password + 1, + login.usernameField + 1, + login.passwordField + 1 + ); + newLogin.QueryInterface(Ci.nsILoginMetaInfo); + newLogin.timeCreated = login.timeCreated + 1; + newLogin.timePasswordChanged = login.timePasswordChanged + 1; + newLogin.timesUsed = login.timesUsed + 1; + return newLogin; +} + +add_task(async function setup() { + let dirSvcPath; + let pathId; + let profilePathSegments; + + // Use a mock service and account name to avoid a Keychain auth. prompt that + // would block the test from finishing if Chrome has already created a matching + // Keychain entry. This allows us to still exercise the keychain lookup code. + // The mock encryption passphrase is used when the Keychain item isn't found. + const mockMacOSKeychain = { + passphrase: "bW96aWxsYWZpcmVmb3g=", + serviceName: "TESTING Chrome Safe Storage", + accountName: "TESTING Chrome", + }; + if (AppConstants.platform == "macosx") { + const { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs" + ); + loginCrypto = new ChromeMacOSLoginCrypto( + mockMacOSKeychain.serviceName, + mockMacOSKeychain.accountName, + mockMacOSKeychain.passphrase + ); + dirSvcPath = "Library/"; + pathId = "ULibDir"; + profilePathSegments = [ + "Application Support", + "Google", + "Chrome", + "Default", + "Login Data", + ]; + } else if (AppConstants.platform == "win") { + const { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule( + "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs" + ); + loginCrypto = new ChromeWindowsLoginCrypto("Chrome"); + dirSvcPath = "AppData/Local/"; + pathId = "LocalAppData"; + profilePathSegments = [ + "Google", + "Chrome", + "User Data", + "Default", + "Login Data", + ]; + } else { + throw new Error("Not implemented"); + } + const dirSvcFile = do_get_file(dirSvcPath); + registerFakePath(pathId, dirSvcFile); + + info(PathUtils.join(dirSvcFile.path, ...profilePathSegments)); + const loginDataFilePath = PathUtils.join( + dirSvcFile.path, + ...profilePathSegments + ); + dbConn = await Sqlite.openConnection({ path: loginDataFilePath }); + + if (AppConstants.platform == "macosx") { + const migrator = await MigrationUtils.getMigrator("chrome"); + Object.assign(migrator, { + _keychainServiceName: mockMacOSKeychain.serviceName, + _keychainAccountName: mockMacOSKeychain.accountName, + _keychainMockPassphrase: mockMacOSKeychain.passphrase, + }); + } + + registerCleanupFunction(() => { + Services.logins.removeAllUserFacingLogins(); + if (loginCrypto.finalize) { + loginCrypto.finalize(); + } + return dbConn.close(); + }); +}); + +add_task(async function test_importIntoEmptyDB() { + for (const login of TEST_LOGINS) { + await promiseSetPassword(login); + } + + const migrator = await MigrationUtils.getMigrator("chrome"); + Assert.ok( + await migrator.isSourceAvailable(), + "Sanity check the source exists" + ); + + let logins = Services.logins.getAllLogins(); + Assert.equal(logins.length, 0, "There are no logins initially"); + + // Migrate the logins. + await promiseMigration( + migrator, + MigrationUtils.resourceTypes.PASSWORDS, + PROFILE, + true + ); + + logins = Services.logins.getAllLogins(); + Assert.equal( + logins.length, + TEST_LOGINS.length, + "Check login count after importing the data" + ); + Assert.equal( + logins.length, + MigrationUtils._importQuantities.logins, + "Check telemetry matches the actual import." + ); + + for (let i = 0; i < TEST_LOGINS.length; i++) { + checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1); + } +}); + +// Test that existing logins for the same primary key don't get overwritten +add_task(async function test_importExistingLogins() { + const migrator = await MigrationUtils.getMigrator("chrome"); + Assert.ok( + await migrator.isSourceAvailable(), + "Sanity check the source exists" + ); + + Services.logins.removeAllUserFacingLogins(); + let logins = Services.logins.getAllLogins(); + Assert.equal( + logins.length, + 0, + "There are no logins after removing all of them" + ); + + const newLogins = []; + + // Create 3 new logins that are different but where the key properties are still the same. + for (let i = 0; i < 3; i++) { + newLogins.push(generateDifferentLogin(TEST_LOGINS[i])); + await Services.logins.addLoginAsync(newLogins[i]); + } + + logins = Services.logins.getAllLogins(); + Assert.equal( + logins.length, + newLogins.length, + "Check login count after the insertion" + ); + + for (let i = 0; i < newLogins.length; i++) { + checkLoginsAreEqual(logins[i], newLogins[i], i + 1); + } + // Migrate the logins. + await promiseMigration( + migrator, + MigrationUtils.resourceTypes.PASSWORDS, + PROFILE, + true + ); + + logins = Services.logins.getAllLogins(); + Assert.equal( + logins.length, + TEST_LOGINS.length, + "Check there are still the same number of logins after re-importing the data" + ); + Assert.equal( + logins.length, + MigrationUtils._importQuantities.logins, + "Check telemetry matches the actual import." + ); + + for (let i = 0; i < newLogins.length; i++) { + checkLoginsAreEqual(logins[i], newLogins[i], i + 1); + } +}); |