tor-browser

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

test_password_store.js (9014B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { LoginRec } = ChromeUtils.importESModule(
      5  "resource://services-sync/engines/passwords.sys.mjs"
      6 );
      7 const { Service } = ChromeUtils.importESModule(
      8  "resource://services-sync/service.sys.mjs"
      9 );
     10 const { SyncedRecordsTelemetry } = ChromeUtils.importESModule(
     11  "resource://services-sync/telemetry.sys.mjs"
     12 );
     13 
     14 async function checkRecord(
     15  name,
     16  record,
     17  expectedCount,
     18  timeCreated,
     19  expectedTimeCreated,
     20  timePasswordChanged,
     21  expectedTimePasswordChanged,
     22  recordIsUpdated
     23 ) {
     24  let engine = Service.engineManager.get("passwords");
     25  let store = engine._store;
     26 
     27  let logins = await Services.logins.searchLoginsAsync({
     28    origin: record.hostname,
     29    formActionOrigin: record.formSubmitURL,
     30  });
     31 
     32  _("Record" + name + ":" + JSON.stringify(logins));
     33  _("Count" + name + ":" + logins.length);
     34 
     35  Assert.equal(logins.length, expectedCount);
     36 
     37  if (expectedCount > 0) {
     38    Assert.ok(!!(await store.getAllIDs())[record.id]);
     39    let stored_record = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
     40 
     41    if (timeCreated !== undefined) {
     42      Assert.equal(stored_record.timeCreated, expectedTimeCreated);
     43    }
     44 
     45    if (timePasswordChanged !== undefined) {
     46      if (recordIsUpdated) {
     47        Assert.greaterOrEqual(
     48          stored_record.timePasswordChanged,
     49          expectedTimePasswordChanged
     50        );
     51      } else {
     52        Assert.equal(
     53          stored_record.timePasswordChanged,
     54          expectedTimePasswordChanged
     55        );
     56      }
     57      return stored_record.timePasswordChanged;
     58    }
     59  } else {
     60    Assert.ok(!(await store.getAllIDs())[record.id]);
     61  }
     62  return undefined;
     63 }
     64 
     65 async function changePassword(
     66  name,
     67  hostname,
     68  password,
     69  expectedCount,
     70  timeCreated,
     71  expectedTimeCreated,
     72  timePasswordChanged,
     73  expectedTimePasswordChanged,
     74  insert,
     75  recordIsUpdated
     76 ) {
     77  const BOGUS_GUID = "zzzzzz" + hostname;
     78  let record = new LoginRec("passwords", BOGUS_GUID);
     79  record.cleartext = {
     80    id: BOGUS_GUID,
     81    hostname,
     82    formSubmitURL: hostname,
     83    username: "john",
     84    password,
     85    usernameField: "username",
     86    passwordField: "password",
     87  };
     88 
     89  if (timeCreated !== undefined) {
     90    record.timeCreated = timeCreated;
     91  }
     92 
     93  if (timePasswordChanged !== undefined) {
     94    record.timePasswordChanged = timePasswordChanged;
     95  }
     96 
     97  let engine = Service.engineManager.get("passwords");
     98  let store = engine._store;
     99 
    100  if (insert) {
    101    let countTelemetry = new SyncedRecordsTelemetry();
    102    Assert.equal(
    103      (await store.applyIncomingBatch([record], countTelemetry)).length,
    104      0
    105    );
    106  }
    107 
    108  return checkRecord(
    109    name,
    110    record,
    111    expectedCount,
    112    timeCreated,
    113    expectedTimeCreated,
    114    timePasswordChanged,
    115    expectedTimePasswordChanged,
    116    recordIsUpdated
    117  );
    118 }
    119 
    120 async function test_apply_records_with_times(
    121  hostname,
    122  timeCreated,
    123  timePasswordChanged
    124 ) {
    125  // The following record is going to be inserted in the store and it needs
    126  // to be found there. Then its timestamps are going to be compared to
    127  // the expected values.
    128  await changePassword(
    129    " ",
    130    hostname,
    131    "password",
    132    1,
    133    timeCreated,
    134    timeCreated,
    135    timePasswordChanged,
    136    timePasswordChanged,
    137    true
    138  );
    139 }
    140 
    141 async function test_apply_multiple_records_with_times() {
    142  // The following records are going to be inserted in the store and they need
    143  // to be found there. Then their timestamps are going to be compared to
    144  // the expected values.
    145  await changePassword(
    146    "A",
    147    "http://foo.a.com",
    148    "password",
    149    1,
    150    undefined,
    151    undefined,
    152    undefined,
    153    undefined,
    154    true
    155  );
    156  await changePassword(
    157    "B",
    158    "http://foo.b.com",
    159    "password",
    160    1,
    161    1000,
    162    1000,
    163    undefined,
    164    undefined,
    165    true
    166  );
    167  await changePassword(
    168    "C",
    169    "http://foo.c.com",
    170    "password",
    171    1,
    172    undefined,
    173    undefined,
    174    1000,
    175    1000,
    176    true
    177  );
    178  await changePassword(
    179    "D",
    180    "http://foo.d.com",
    181    "password",
    182    1,
    183    1000,
    184    1000,
    185    1000,
    186    1000,
    187    true
    188  );
    189 
    190  // The following records are not going to be inserted in the store and they
    191  // are not going to be found there.
    192  await changePassword(
    193    "NotInStoreA",
    194    "http://foo.aaaa.com",
    195    "password",
    196    0,
    197    undefined,
    198    undefined,
    199    undefined,
    200    undefined,
    201    false
    202  );
    203  await changePassword(
    204    "NotInStoreB",
    205    "http://foo.bbbb.com",
    206    "password",
    207    0,
    208    1000,
    209    1000,
    210    undefined,
    211    undefined,
    212    false
    213  );
    214  await changePassword(
    215    "NotInStoreC",
    216    "http://foo.cccc.com",
    217    "password",
    218    0,
    219    undefined,
    220    undefined,
    221    1000,
    222    1000,
    223    false
    224  );
    225  await changePassword(
    226    "NotInStoreD",
    227    "http://foo.dddd.com",
    228    "password",
    229    0,
    230    1000,
    231    1000,
    232    1000,
    233    1000,
    234    false
    235  );
    236 }
    237 
    238 async function test_apply_same_record_with_different_times() {
    239  // The following record is going to be inserted multiple times in the store
    240  // and it needs to be found there. Then its timestamps are going to be
    241  // compared to the expected values.
    242 
    243  /* eslint-disable no-unused-vars */
    244  /* The eslint linter thinks that timePasswordChanged is unused, even though
    245     it is passed as an argument to changePassword. */
    246  var timePasswordChanged = 100;
    247  timePasswordChanged = await changePassword(
    248    "A",
    249    "http://a.tn",
    250    "password",
    251    1,
    252    100,
    253    100,
    254    100,
    255    timePasswordChanged,
    256    true
    257  );
    258  timePasswordChanged = await changePassword(
    259    "A",
    260    "http://a.tn",
    261    "password",
    262    1,
    263    100,
    264    100,
    265    800,
    266    timePasswordChanged,
    267    true,
    268    true
    269  );
    270  timePasswordChanged = await changePassword(
    271    "A",
    272    "http://a.tn",
    273    "password",
    274    1,
    275    500,
    276    100,
    277    800,
    278    timePasswordChanged,
    279    true,
    280    true
    281  );
    282  timePasswordChanged = await changePassword(
    283    "A",
    284    "http://a.tn",
    285    "password2",
    286    1,
    287    500,
    288    100,
    289    1536213005222,
    290    timePasswordChanged,
    291    true,
    292    true
    293  );
    294  timePasswordChanged = await changePassword(
    295    "A",
    296    "http://a.tn",
    297    "password2",
    298    1,
    299    500,
    300    100,
    301    800,
    302    timePasswordChanged,
    303    true,
    304    true
    305  );
    306  /* eslint-enable no-unused-vars */
    307 }
    308 
    309 async function test_LoginRec_toString(store, recordData) {
    310  let rec = await store.createRecord(recordData.id);
    311  ok(rec);
    312  ok(!rec.toString().includes(rec.password));
    313 }
    314 
    315 add_task(async function run_test() {
    316  const BOGUS_GUID_A = "zzzzzzzzzzzz";
    317  const BOGUS_GUID_B = "yyyyyyyyyyyy";
    318  let recordA = new LoginRec("passwords", BOGUS_GUID_A);
    319  let recordB = new LoginRec("passwords", BOGUS_GUID_B);
    320  recordA.cleartext = {
    321    id: BOGUS_GUID_A,
    322    hostname: "http://foo.bar.com",
    323    formSubmitURL: "http://foo.bar.com",
    324    httpRealm: "secure",
    325    username: "john",
    326    password: "smith",
    327    usernameField: "username",
    328    passwordField: "password",
    329  };
    330  recordB.cleartext = {
    331    id: BOGUS_GUID_B,
    332    hostname: "http://foo.baz.com",
    333    formSubmitURL: "http://foo.baz.com",
    334    username: "john",
    335    password: "smith",
    336    usernameField: "username",
    337    passwordField: "password",
    338    unknownStr: "an unknown string from another field",
    339  };
    340 
    341  let engine = Service.engineManager.get("passwords");
    342  let store = engine._store;
    343 
    344  try {
    345    let countTelemetry = new SyncedRecordsTelemetry();
    346    Assert.equal(
    347      (await store.applyIncomingBatch([recordA, recordB], countTelemetry))
    348        .length,
    349      0
    350    );
    351 
    352    // Only the good record makes it to Services.logins.
    353    let badLogins = await Services.logins.searchLoginsAsync({
    354      origin: recordA.hostname,
    355      formActionOrigin: recordA.formSubmitURL,
    356      httpRealm: recordA.httpRealm,
    357    });
    358    let goodLogins = await Services.logins.searchLoginsAsync({
    359      origin: recordB.hostname,
    360      formActionOrigin: recordB.formSubmitURL,
    361    });
    362 
    363    _("Bad: " + JSON.stringify(badLogins));
    364    _("Good: " + JSON.stringify(goodLogins));
    365    _("Count: " + badLogins.length + ", " + goodLogins.length);
    366 
    367    Assert.equal(goodLogins.length, 1);
    368    Assert.equal(badLogins.length, 0);
    369 
    370    // applyIncoming should've put any unknown fields from the server
    371    // into a catch-all unknownFields field
    372    Assert.equal(
    373      goodLogins[0].unknownFields,
    374      JSON.stringify({
    375        unknownStr: "an unknown string from another field",
    376      })
    377    );
    378 
    379    Assert.ok(!!(await store.getAllIDs())[BOGUS_GUID_B]);
    380    Assert.ok(!(await store.getAllIDs())[BOGUS_GUID_A]);
    381 
    382    await test_LoginRec_toString(store, recordB);
    383 
    384    await test_apply_records_with_times(
    385      "http://afoo.baz.com",
    386      undefined,
    387      undefined
    388    );
    389    await test_apply_records_with_times("http://bfoo.baz.com", 1000, undefined);
    390    await test_apply_records_with_times("http://cfoo.baz.com", undefined, 2000);
    391    await test_apply_records_with_times("http://dfoo.baz.com", 1000, 2000);
    392 
    393    await test_apply_multiple_records_with_times();
    394 
    395    await test_apply_same_record_with_different_times();
    396  } finally {
    397    await store.wipe();
    398  }
    399 });