tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_Chrome_passwords.js (10166B)


      1 "use strict";
      2 
      3 const PROFILE = {
      4  id: "Default",
      5  name: "Person 1",
      6 };
      7 
      8 const TEST_LOGINS = [
      9  {
     10    id: 1, // id of the row in the chrome login db
     11    username: "username",
     12    password: "password",
     13    origin: "https://c9.io",
     14    formActionOrigin: "https://c9.io",
     15    httpRealm: null,
     16    usernameField: "inputEmail",
     17    passwordField: "inputPassword",
     18    timeCreated: 1437418416037,
     19    timePasswordChanged: 1437418416037,
     20    timesUsed: 1,
     21  },
     22  {
     23    id: 2,
     24    username: "username@gmail.com",
     25    password: "password2",
     26    origin: "https://accounts.google.com",
     27    formActionOrigin: "https://accounts.google.com",
     28    httpRealm: null,
     29    usernameField: "Email",
     30    passwordField: "Passwd",
     31    timeCreated: 1437418446598,
     32    timePasswordChanged: 1437418446598,
     33    timesUsed: 6,
     34  },
     35  {
     36    id: 3,
     37    username: "username",
     38    password: "password3",
     39    origin: "https://www.facebook.com",
     40    formActionOrigin: "https://www.facebook.com",
     41    httpRealm: null,
     42    usernameField: "email",
     43    passwordField: "pass",
     44    timeCreated: 1437418478851,
     45    timePasswordChanged: 1437418478851,
     46    timesUsed: 1,
     47  },
     48  {
     49    id: 4,
     50    username: "user",
     51    password: "اقرأPÀßwörd",
     52    origin: "http://httpbin.org",
     53    formActionOrigin: null,
     54    httpRealm: "me@kennethreitz.com", // Digest auth.
     55    usernameField: "",
     56    passwordField: "",
     57    timeCreated: 1437787462368,
     58    timePasswordChanged: 1437787462368,
     59    timesUsed: 1,
     60  },
     61  {
     62    id: 5,
     63    username: "buser",
     64    password: "bpassword",
     65    origin: "http://httpbin.org",
     66    formActionOrigin: null,
     67    httpRealm: "Fake Realm", // Basic auth.
     68    usernameField: "",
     69    passwordField: "",
     70    timeCreated: 1437787539233,
     71    timePasswordChanged: 1437787539233,
     72    timesUsed: 1,
     73  },
     74  {
     75    id: 6,
     76    username: "username",
     77    password: "password6",
     78    origin: "https://www.example.com",
     79    formActionOrigin: "", // NULL `action_url`
     80    httpRealm: null,
     81    usernameField: "",
     82    passwordField: "pass",
     83    timeCreated: 1557291348878,
     84    timePasswordChanged: 1557291348878,
     85    timesUsed: 1,
     86  },
     87  {
     88    id: 7,
     89    version: "v10",
     90    username: "username",
     91    password: "password",
     92    origin: "https://v10.io",
     93    formActionOrigin: "https://v10.io",
     94    httpRealm: null,
     95    usernameField: "inputEmail",
     96    passwordField: "inputPassword",
     97    timeCreated: 1437418416037,
     98    timePasswordChanged: 1437418416037,
     99    timesUsed: 1,
    100  },
    101 ];
    102 
    103 var loginCrypto;
    104 var dbConn;
    105 
    106 async function promiseSetPassword(login) {
    107  const encryptedString = await loginCrypto.encryptData(
    108    login.password,
    109    login.version
    110  );
    111  info(`promiseSetPassword: ${encryptedString}`);
    112  const passwordValue = new Uint8Array(
    113    loginCrypto.stringToArray(encryptedString)
    114  );
    115  return dbConn.execute(
    116    `UPDATE logins
    117                         SET password_value = :password_value
    118                         WHERE rowid = :rowid
    119                        `,
    120    { password_value: passwordValue, rowid: login.id }
    121  );
    122 }
    123 
    124 function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
    125  passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
    126 
    127  Assert.equal(
    128    passwordManagerLogin.username,
    129    chromeLogin.username,
    130    "The two logins ID " + id + " have the same username"
    131  );
    132  Assert.equal(
    133    passwordManagerLogin.password,
    134    chromeLogin.password,
    135    "The two logins ID " + id + " have the same password"
    136  );
    137  Assert.equal(
    138    passwordManagerLogin.origin,
    139    chromeLogin.origin,
    140    "The two logins ID " + id + " have the same origin"
    141  );
    142  Assert.equal(
    143    passwordManagerLogin.formActionOrigin,
    144    chromeLogin.formActionOrigin,
    145    "The two logins ID " + id + " have the same formActionOrigin"
    146  );
    147  Assert.equal(
    148    passwordManagerLogin.httpRealm,
    149    chromeLogin.httpRealm,
    150    "The two logins ID " + id + " have the same httpRealm"
    151  );
    152  Assert.equal(
    153    passwordManagerLogin.usernameField,
    154    chromeLogin.usernameField,
    155    "The two logins ID " + id + " have the same usernameElement"
    156  );
    157  Assert.equal(
    158    passwordManagerLogin.passwordField,
    159    chromeLogin.passwordField,
    160    "The two logins ID " + id + " have the same passwordElement"
    161  );
    162  Assert.equal(
    163    passwordManagerLogin.timeCreated,
    164    chromeLogin.timeCreated,
    165    "The two logins ID " + id + " have the same timeCreated"
    166  );
    167  Assert.equal(
    168    passwordManagerLogin.timePasswordChanged,
    169    chromeLogin.timePasswordChanged,
    170    "The two logins ID " + id + " have the same timePasswordChanged"
    171  );
    172  Assert.equal(
    173    passwordManagerLogin.timesUsed,
    174    chromeLogin.timesUsed,
    175    "The two logins ID " + id + " have the same timesUsed"
    176  );
    177 }
    178 
    179 function generateDifferentLogin(login) {
    180  const newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(
    181    Ci.nsILoginInfo
    182  );
    183 
    184  newLogin.init(
    185    login.origin,
    186    login.formActionOrigin,
    187    null,
    188    login.username,
    189    login.password + 1,
    190    login.usernameField + 1,
    191    login.passwordField + 1
    192  );
    193  newLogin.QueryInterface(Ci.nsILoginMetaInfo);
    194  newLogin.timeCreated = login.timeCreated + 1;
    195  newLogin.timePasswordChanged = login.timePasswordChanged + 1;
    196  newLogin.timesUsed = login.timesUsed + 1;
    197  return newLogin;
    198 }
    199 
    200 add_task(async function setup() {
    201  let dirSvcPath;
    202  let pathId;
    203  let profilePathSegments;
    204 
    205  // Use a mock service and account name to avoid a Keychain auth. prompt that
    206  // would block the test from finishing if Chrome has already created a matching
    207  // Keychain entry. This allows us to still exercise the keychain lookup code.
    208  // The mock encryption passphrase is used when the Keychain item isn't found.
    209  const mockMacOSKeychain = {
    210    passphrase: "bW96aWxsYWZpcmVmb3g=",
    211    serviceName: "TESTING Chrome Safe Storage",
    212    accountName: "TESTING Chrome",
    213  };
    214  if (AppConstants.platform == "macosx") {
    215    const { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule(
    216      "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs"
    217    );
    218    loginCrypto = new ChromeMacOSLoginCrypto(
    219      mockMacOSKeychain.serviceName,
    220      mockMacOSKeychain.accountName,
    221      mockMacOSKeychain.passphrase
    222    );
    223    dirSvcPath = "Library/";
    224    pathId = "ULibDir";
    225    profilePathSegments = [
    226      "Application Support",
    227      "Google",
    228      "Chrome",
    229      "Default",
    230      "Login Data",
    231    ];
    232  } else if (AppConstants.platform == "win") {
    233    const { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule(
    234      "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs"
    235    );
    236    loginCrypto = new ChromeWindowsLoginCrypto("Chrome");
    237    dirSvcPath = "AppData/Local/";
    238    pathId = "LocalAppData";
    239    profilePathSegments = [
    240      "Google",
    241      "Chrome",
    242      "User Data",
    243      "Default",
    244      "Login Data",
    245    ];
    246  } else {
    247    throw new Error("Not implemented");
    248  }
    249  const dirSvcFile = do_get_file(dirSvcPath);
    250  registerFakePath(pathId, dirSvcFile);
    251 
    252  info(PathUtils.join(dirSvcFile.path, ...profilePathSegments));
    253  const loginDataFilePath = PathUtils.join(
    254    dirSvcFile.path,
    255    ...profilePathSegments
    256  );
    257  dbConn = await Sqlite.openConnection({ path: loginDataFilePath });
    258 
    259  if (AppConstants.platform == "macosx") {
    260    const migrator = await MigrationUtils.getMigrator("chrome");
    261    Object.assign(migrator, {
    262      _keychainServiceName: mockMacOSKeychain.serviceName,
    263      _keychainAccountName: mockMacOSKeychain.accountName,
    264      _keychainMockPassphrase: mockMacOSKeychain.passphrase,
    265    });
    266  }
    267 
    268  registerCleanupFunction(() => {
    269    Services.logins.removeAllUserFacingLogins();
    270    if (loginCrypto.finalize) {
    271      loginCrypto.finalize();
    272    }
    273    return dbConn.close();
    274  });
    275 });
    276 
    277 add_task(async function test_importIntoEmptyDB() {
    278  for (const login of TEST_LOGINS) {
    279    await promiseSetPassword(login);
    280  }
    281 
    282  const migrator = await MigrationUtils.getMigrator("chrome");
    283  Assert.ok(
    284    await migrator.isSourceAvailable(),
    285    "Sanity check the source exists"
    286  );
    287 
    288  let logins = await Services.logins.getAllLogins();
    289  Assert.equal(logins.length, 0, "There are no logins initially");
    290 
    291  // Migrate the logins.
    292  await promiseMigration(
    293    migrator,
    294    MigrationUtils.resourceTypes.PASSWORDS,
    295    PROFILE,
    296    true
    297  );
    298 
    299  logins = await Services.logins.getAllLogins();
    300  Assert.equal(
    301    logins.length,
    302    TEST_LOGINS.length,
    303    "Check login count after importing the data"
    304  );
    305  Assert.equal(
    306    logins.length,
    307    MigrationUtils._importQuantities.logins,
    308    "Check telemetry matches the actual import."
    309  );
    310 
    311  for (let i = 0; i < TEST_LOGINS.length; i++) {
    312    checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1);
    313  }
    314 });
    315 
    316 // Test that existing logins for the same primary key don't get overwritten
    317 add_task(async function test_importExistingLogins() {
    318  const migrator = await MigrationUtils.getMigrator("chrome");
    319  Assert.ok(
    320    await migrator.isSourceAvailable(),
    321    "Sanity check the source exists"
    322  );
    323 
    324  Services.logins.removeAllUserFacingLogins();
    325  let logins = await Services.logins.getAllLogins();
    326  Assert.equal(
    327    logins.length,
    328    0,
    329    "There are no logins after removing all of them"
    330  );
    331 
    332  const newLogins = [];
    333 
    334  // Create 3 new logins that are different but where the key properties are still the same.
    335  for (let i = 0; i < 3; i++) {
    336    newLogins.push(generateDifferentLogin(TEST_LOGINS[i]));
    337    await Services.logins.addLoginAsync(newLogins[i]);
    338  }
    339 
    340  logins = await Services.logins.getAllLogins();
    341  Assert.equal(
    342    logins.length,
    343    newLogins.length,
    344    "Check login count after the insertion"
    345  );
    346 
    347  for (let i = 0; i < newLogins.length; i++) {
    348    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
    349  }
    350  // Migrate the logins.
    351  await promiseMigration(
    352    migrator,
    353    MigrationUtils.resourceTypes.PASSWORDS,
    354    PROFILE,
    355    true
    356  );
    357 
    358  logins = await Services.logins.getAllLogins();
    359  Assert.equal(
    360    logins.length,
    361    TEST_LOGINS.length,
    362    "Check there are still the same number of logins after re-importing the data"
    363  );
    364  Assert.equal(
    365    logins.length,
    366    MigrationUtils._importQuantities.logins,
    367    "Check telemetry matches the actual import."
    368  );
    369 
    370  for (let i = 0; i < newLogins.length; i++) {
    371    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
    372  }
    373 });