summaryrefslogtreecommitdiffstats
path: root/browser/components/migration/tests/unit/test_IE7_passwords.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/migration/tests/unit/test_IE7_passwords.js493
1 files changed, 493 insertions, 0 deletions
diff --git a/browser/components/migration/tests/unit/test_IE7_passwords.js b/browser/components/migration/tests/unit/test_IE7_passwords.js
new file mode 100644
index 0000000000..2c372fa343
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_IE7_passwords.js
@@ -0,0 +1,493 @@
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ OSCrypto: "resource://gre/modules/OSCrypto_win.sys.mjs",
+});
+
+const IE7_FORM_PASSWORDS_MIGRATOR_NAME = "IE7FormPasswords";
+const LOGINS_KEY =
+ "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
+const EXTENSION = "-backup";
+const TESTED_WEBSITES = {
+ twitter: {
+ uri: makeURI("https://twitter.com"),
+ hash: "A89D42BC6406E27265B1AD0782B6F376375764A301",
+ data: [
+ 12, 0, 0, 0, 56, 0, 0, 0, 38, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 36, 67, 124,
+ 118, 212, 208, 1, 8, 0, 0, 0, 18, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208,
+ 1, 9, 0, 0, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104,
+ 0, 0, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, 0, 0,
+ 0,
+ ],
+ logins: [
+ {
+ username: "abcdefgh",
+ password: "123456789",
+ origin: "https://twitter.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439325854000,
+ timeLastUsed: 1439325854000,
+ timePasswordChanged: 1439325854000,
+ timesUsed: 1,
+ },
+ ],
+ },
+ facebook: {
+ uri: makeURI("https://www.facebook.com/"),
+ hash: "EF44D3E034009CB0FD1B1D81A1FF3F3335213BD796",
+ data: [
+ 12, 0, 0, 0, 152, 0, 0, 0, 160, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 182, 125, 18,
+ 121, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 88, 182, 125, 18, 121, 212,
+ 208, 1, 9, 0, 0, 0, 40, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0,
+ 0, 0, 60, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0, 0, 0, 80, 0,
+ 0, 0, 45, 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 100, 0, 0, 0, 45,
+ 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 120, 0, 0, 0, 28, 10, 193, 80,
+ 121, 212, 208, 1, 9, 0, 0, 0, 140, 0, 0, 0, 28, 10, 193, 80, 121, 212,
+ 208, 1, 9, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0,
+ 101, 0, 48, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114,
+ 0, 100, 0, 48, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0,
+ 109, 0, 101, 0, 49, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111,
+ 0, 114, 0, 100, 0, 49, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0,
+ 97, 0, 109, 0, 101, 0, 50, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0,
+ 111, 0, 114, 0, 100, 0, 50, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110,
+ 0, 97, 0, 109, 0, 101, 0, 51, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119,
+ 0, 111, 0, 114, 0, 100, 0, 51, 0, 0, 0,
+ ],
+ logins: [
+ {
+ username: "username0",
+ password: "password0",
+ origin: "https://www.facebook.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439326966000,
+ timeLastUsed: 1439326966000,
+ timePasswordChanged: 1439326966000,
+ timesUsed: 1,
+ },
+ {
+ username: "username1",
+ password: "password1",
+ origin: "https://www.facebook.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439326997000,
+ timeLastUsed: 1439326997000,
+ timePasswordChanged: 1439326997000,
+ timesUsed: 1,
+ },
+ {
+ username: "username2",
+ password: "password2",
+ origin: "https://www.facebook.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439327040000,
+ timeLastUsed: 1439327040000,
+ timePasswordChanged: 1439327040000,
+ timesUsed: 1,
+ },
+ {
+ username: "username3",
+ password: "password3",
+ origin: "https://www.facebook.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439327070000,
+ timeLastUsed: 1439327070000,
+ timePasswordChanged: 1439327070000,
+ timesUsed: 1,
+ },
+ ],
+ },
+ live: {
+ uri: makeURI("https://login.live.com/"),
+ hash: "7B506F2D6B81D939A8E0456F036EE8970856FF705E",
+ data: [
+ 12, 0, 0, 0, 56, 0, 0, 0, 44, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 17, 219, 140,
+ 148, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 212, 17, 219, 140, 148, 212,
+ 208, 1, 11, 0, 0, 0, 114, 0, 105, 0, 97, 0, 100, 0, 104, 0, 49, 6, 74, 6,
+ 39, 6, 54, 6, 0, 0, 39, 6, 66, 6, 49, 6, 35, 6, 80, 0, 192, 0, 223, 0,
+ 119, 0, 246, 0, 114, 0, 100, 0, 0, 0,
+ ],
+ logins: [
+ {
+ username: "riadhرياض",
+ password: "اقرأPÀßwörd",
+ origin: "https://login.live.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439338767000,
+ timeLastUsed: 1439338767000,
+ timePasswordChanged: 1439338767000,
+ timesUsed: 1,
+ },
+ ],
+ },
+ reddit: {
+ uri: makeURI("http://www.reddit.com/"),
+ hash: "B644028D1C109A91EC2C4B9D1F145E55A1FAE42065",
+ data: [
+ 12, 0, 0, 0, 152, 0, 0, 0, 212, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 8, 234, 114,
+ 153, 212, 208, 1, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 0, 97, 93, 131, 116, 153, 212, 208, 1, 3, 0, 0, 0, 14, 0, 0,
+ 0, 97, 93, 131, 116, 153, 212, 208, 1, 16, 0, 0, 0, 48, 0, 0, 0, 88, 150,
+ 78, 174, 153, 212, 208, 1, 4, 0, 0, 0, 58, 0, 0, 0, 88, 150, 78, 174, 153,
+ 212, 208, 1, 29, 0, 0, 0, 118, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208,
+ 1, 15, 0, 0, 0, 150, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208, 1, 30, 0,
+ 0, 0, 97, 0, 0, 0, 0, 0, 252, 140, 173, 138, 146, 48, 0, 0, 66, 0, 105, 0,
+ 116, 0, 116, 0, 101, 0, 32, 0, 98, 0, 101, 0, 115, 0, 116, 0, 228, 0, 116,
+ 0, 105, 0, 103, 0, 101, 0, 110, 0, 0, 0, 205, 145, 110, 127, 198, 91, 1,
+ 120, 0, 0, 31, 4, 48, 4, 64, 4, 62, 4, 59, 4, 76, 4, 32, 0, 67, 4, 65, 4,
+ 63, 4, 53, 4, 72, 4, 61, 4, 62, 4, 32, 0, 65, 4, 49, 4, 64, 4, 62, 4, 72,
+ 4, 53, 4, 61, 4, 46, 0, 32, 0, 18, 4, 62, 4, 57, 4, 66, 4, 56, 4, 0, 0,
+ 40, 6, 51, 6, 69, 6, 32, 0, 39, 6, 68, 6, 68, 6, 71, 6, 32, 0, 39, 6, 68,
+ 6, 49, 6, 45, 6, 69, 6, 70, 6, 0, 0, 118, 0, 101, 0, 117, 0, 105, 0, 108,
+ 0, 108, 0, 101, 0, 122, 0, 32, 0, 108, 0, 101, 0, 32, 0, 118, 0, 233, 0,
+ 114, 0, 105, 0, 102, 0, 105, 0, 101, 0, 114, 0, 32, 0, 224, 0, 32, 0, 110,
+ 0, 111, 0, 117, 0, 118, 0, 101, 0, 97, 0, 117, 0, 0, 0,
+ ],
+ logins: [
+ // This login is present in the data, but should be stripped out
+ // by the validation rules of the importer:
+ // {
+ // "username": "a",
+ // "password": "",
+ // "origin": "http://www.reddit.com",
+ // "formActionOrigin": "",
+ // "httpRealm": null,
+ // "usernameField": "",
+ // "passwordField": ""
+ // },
+ {
+ username: "購読を",
+ password: "Bitte bestätigen",
+ origin: "http://www.reddit.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439340874000,
+ timeLastUsed: 1439340874000,
+ timePasswordChanged: 1439340874000,
+ timesUsed: 1,
+ },
+ {
+ username: "重置密码",
+ password: "Пароль успешно сброшен. Войти",
+ origin: "http://www.reddit.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439340971000,
+ timeLastUsed: 1439340971000,
+ timePasswordChanged: 1439340971000,
+ timesUsed: 1,
+ },
+ {
+ username: "بسم الله الرحمن",
+ password: "veuillez le vérifier à nouveau",
+ origin: "http://www.reddit.com",
+ formActionOrigin: "",
+ httpRealm: null,
+ usernameField: "",
+ passwordField: "",
+ timeCreated: 1439341166000,
+ timeLastUsed: 1439341166000,
+ timePasswordChanged: 1439341166000,
+ timesUsed: 1,
+ },
+ ],
+ },
+};
+
+const TESTED_URLS = [
+ "http://a.foo.com",
+ "http://b.foo.com",
+ "http://c.foo.com",
+ "http://www.test.net",
+ "http://www.test.net/home",
+ "http://www.test.net/index",
+ "https://a.bar.com",
+ "https://b.bar.com",
+ "https://c.bar.com",
+];
+
+var nsIWindowsRegKey = Ci.nsIWindowsRegKey;
+var Storage2Key;
+
+/*
+ * If the key value exists, it's going to be backed up and replaced, so the value could be restored.
+ * Otherwise a new value is going to be created.
+ */
+function backupAndStore(key, name, value) {
+ if (key.hasValue(name)) {
+ // backup the the current value
+ let type = key.getValueType(name);
+ // create a new value using use the current value name followed by EXTENSION as its new name
+ switch (type) {
+ case nsIWindowsRegKey.TYPE_STRING:
+ key.writeStringValue(name + EXTENSION, key.readStringValue(name));
+ break;
+ case nsIWindowsRegKey.TYPE_BINARY:
+ key.writeBinaryValue(name + EXTENSION, key.readBinaryValue(name));
+ break;
+ case nsIWindowsRegKey.TYPE_INT:
+ key.writeIntValue(name + EXTENSION, key.readIntValue(name));
+ break;
+ case nsIWindowsRegKey.TYPE_INT64:
+ key.writeInt64Value(name + EXTENSION, key.readInt64Value(name));
+ break;
+ }
+ }
+ key.writeBinaryValue(name, value);
+}
+
+// Remove all values where their names are members of the names array from the key of registry
+function removeAllValues(key, names) {
+ for (let name of names) {
+ key.removeValue(name);
+ }
+}
+
+// Restore all the backed up values
+function restore(key) {
+ let count = key.valueCount;
+ let names = []; // the names of the key values
+ for (let i = 0; i < count; ++i) {
+ names.push(key.getValueName(i));
+ }
+
+ for (let name of names) {
+ // backed up values have EXTENSION at the end of their names
+ if (name.lastIndexOf(EXTENSION) == name.length - EXTENSION.length) {
+ let valueName = name.substr(0, name.length - EXTENSION.length);
+ let type = key.getValueType(name);
+ // create a new value using the name before the backup and removed the backed up one
+ switch (type) {
+ case nsIWindowsRegKey.TYPE_STRING:
+ key.writeStringValue(valueName, key.readStringValue(name));
+ key.removeValue(name);
+ break;
+ case nsIWindowsRegKey.TYPE_BINARY:
+ key.writeBinaryValue(valueName, key.readBinaryValue(name));
+ key.removeValue(name);
+ break;
+ case nsIWindowsRegKey.TYPE_INT:
+ key.writeIntValue(valueName, key.readIntValue(name));
+ key.removeValue(name);
+ break;
+ case nsIWindowsRegKey.TYPE_INT64:
+ key.writeInt64Value(valueName, key.readInt64Value(name));
+ key.removeValue(name);
+ break;
+ }
+ }
+ }
+}
+
+function checkLoginsAreEqual(passwordManagerLogin, IELogin, id) {
+ passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
+ for (let attribute in IELogin) {
+ Assert.equal(
+ passwordManagerLogin[attribute],
+ IELogin[attribute],
+ "The two logins ID " + id + " have the same " + attribute
+ );
+ }
+}
+
+function createRegistryPath(path) {
+ let loginPath = path.split("\\");
+ let parentKey =
+ Cc["@mozilla.org/windows-registry-key;1"].createInstance(nsIWindowsRegKey);
+ let currentPath = [];
+ for (let currentKey of loginPath) {
+ parentKey.open(
+ nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ currentPath.join("\\"),
+ nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ if (!parentKey.hasChild(currentKey)) {
+ parentKey.createChild(currentKey, 0);
+ }
+ currentPath.push(currentKey);
+ parentKey.close();
+ }
+}
+
+async function getFirstResourceOfType(type) {
+ let migrator = await MigrationUtils.getMigrator("ie");
+ let migrators = migrator.getResources();
+ for (let m of migrators) {
+ if (m.name == IE7_FORM_PASSWORDS_MIGRATOR_NAME && m.type == type) {
+ return m;
+ }
+ }
+ throw new Error("failed to find the " + type + " migrator");
+}
+
+function makeURI(aURL) {
+ return Services.io.newURI(aURL);
+}
+
+add_task(async function setup() {
+ if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
+ await Assert.rejects(
+ getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS),
+ /failed to find/,
+ "The migrator doesn't exist for win8+"
+ );
+ return;
+ }
+ // create the path to Storage2 in the registry if it doest exist.
+ createRegistryPath(LOGINS_KEY);
+ Storage2Key =
+ Cc["@mozilla.org/windows-registry-key;1"].createInstance(nsIWindowsRegKey);
+ Storage2Key.open(
+ nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ LOGINS_KEY,
+ nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ // create a dummy value otherwise the migrator doesn't exist
+ if (!Storage2Key.hasValue("dummy")) {
+ Storage2Key.writeBinaryValue("dummy", "dummy");
+ }
+});
+
+add_task(async function test_passwordsNotAvailable() {
+ if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
+ return;
+ }
+
+ let migrator = await getFirstResourceOfType(
+ MigrationUtils.resourceTypes.PASSWORDS
+ );
+ Assert.ok(migrator.exists, "The migrator has to exist");
+ let logins = Services.logins.getAllLogins();
+ Assert.equal(
+ logins.length,
+ 0,
+ "There are no logins at the beginning of the test"
+ );
+
+ let uris = []; // the uris of the migrated logins
+ for (let url of TESTED_URLS) {
+ uris.push(makeURI(url));
+ // in this test, there is no IE login data in the registry, so after the migration, the number
+ // of logins in the store should be 0
+ await migrator._migrateURIs(uris);
+ logins = Services.logins.getAllLogins();
+ Assert.equal(
+ logins.length,
+ 0,
+ "There are no logins after doing the migration without adding values to the registry"
+ );
+ }
+});
+
+add_task(async function test_passwordsAvailable() {
+ if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
+ return;
+ }
+
+ let crypto = new OSCrypto();
+ let hashes = []; // the hashes of all migrator websites, this is going to be used for the clean up
+
+ registerCleanupFunction(() => {
+ Services.logins.removeAllUserFacingLogins();
+ logins = Services.logins.getAllLogins();
+ Assert.equal(logins.length, 0, "There are no logins after the cleanup");
+ // remove all the values created in this test from the registry
+ removeAllValues(Storage2Key, hashes);
+ // restore all backed up values
+ restore(Storage2Key);
+
+ // clean the dummy value
+ if (Storage2Key.hasValue("dummy")) {
+ Storage2Key.removeValue("dummy");
+ }
+ Storage2Key.close();
+ crypto.finalize();
+ });
+
+ let migrator = await getFirstResourceOfType(
+ MigrationUtils.resourceTypes.PASSWORDS
+ );
+ Assert.ok(migrator.exists, "The migrator has to exist");
+ let logins = Services.logins.getAllLogins();
+ Assert.equal(
+ logins.length,
+ 0,
+ "There are no logins at the beginning of the test"
+ );
+
+ let uris = []; // the uris of the migrated logins
+
+ let loginCount = 0;
+ for (let current in TESTED_WEBSITES) {
+ let website = TESTED_WEBSITES[current];
+ // backup the current the registry value if it exists and replace the existing value/create a
+ // new value with the encrypted data
+ backupAndStore(
+ Storage2Key,
+ website.hash,
+ crypto.encryptData(crypto.arrayToString(website.data), website.uri.spec)
+ );
+ Assert.ok(migrator.exists, "The migrator has to exist");
+ uris.push(website.uri);
+ hashes.push(website.hash);
+
+ await migrator._migrateURIs(uris);
+ logins = Services.logins.getAllLogins();
+ // check that the number of logins in the password manager has increased as expected which means
+ // that all the values for the current website were imported
+ loginCount += website.logins.length;
+ Assert.equal(
+ logins.length,
+ loginCount,
+ "The number of logins has increased after the migration"
+ );
+ // NB: because telemetry records any login data passed to the login manager, it
+ // also gets told about logins that are duplicates or invalid (for one reason
+ // or another) and so its counts might exceed those of the login manager itself.
+ Assert.greaterOrEqual(
+ MigrationUtils._importQuantities.logins,
+ loginCount,
+ "Telemetry quantities equal or exceed the actual import."
+ );
+ // Reset - this normally happens at the start of a new migration, but we're calling
+ // the migrator directly so can't rely on that:
+ MigrationUtils._importQuantities.logins = 0;
+
+ let startIndex = loginCount - website.logins.length;
+ // compares the imported password manager logins with their expected logins
+ for (let i = 0; i < website.logins.length; i++) {
+ checkLoginsAreEqual(
+ logins[startIndex + i],
+ website.logins[i],
+ " " + current + " - " + i + " "
+ );
+ }
+ }
+});