tor-browser

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

browser_loadPKCS11Module_ui.js (8532B)


      1 // Any copyright is dedicated to the Public Domain.
      2 // http://creativecommons.org/publicdomain/zero/1.0/
      3 "use strict";
      4 
      5 // Tests the dialog used for loading PKCS #11 modules.
      6 
      7 const { MockRegistrar } = ChromeUtils.importESModule(
      8  "resource://testing-common/MockRegistrar.sys.mjs"
      9 );
     10 
     11 const gMockPKCS11ModuleDB = {
     12  addModuleCallCount: 0,
     13  expectedLibPath: "",
     14  expectedModuleName: "",
     15  throwOnAddModule: false,
     16 
     17  addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
     18    this.addModuleCallCount++;
     19    Assert.equal(
     20      moduleName,
     21      this.expectedModuleName,
     22      "addModule: Name given should be what's in the name textbox"
     23    );
     24    Assert.equal(
     25      libraryFullPath,
     26      this.expectedLibPath,
     27      "addModule: Path given should be what's in the path textbox"
     28    );
     29    Assert.equal(
     30      cryptoMechanismFlags,
     31      0,
     32      "addModule: No crypto mechanism flags should be passed"
     33    );
     34    Assert.equal(cipherFlags, 0, "addModule: No cipher flags should be passed");
     35 
     36    if (this.throwOnAddModule) {
     37      throw new Error(`addModule: Throwing exception`);
     38    }
     39  },
     40 
     41  deleteModule() {
     42    Assert.ok(false, `deleteModule: should not be called`);
     43  },
     44 
     45  getInternal() {
     46    throw new Error("not expecting getInternal() to be called");
     47  },
     48 
     49  getInternalFIPS() {
     50    throw new Error("not expecting getInternalFIPS() to be called");
     51  },
     52 
     53  listModules() {
     54    throw new Error("not expecting listModules() to be called");
     55  },
     56 
     57  get canToggleFIPS() {
     58    throw new Error("not expecting get canToggleFIPS() to be called");
     59  },
     60 
     61  toggleFIPSMode() {
     62    throw new Error("not expecting toggleFIPSMode() to be called");
     63  },
     64 
     65  get isFIPSEnabled() {
     66    throw new Error("not expecting get isFIPSEnabled() to be called");
     67  },
     68 
     69  QueryInterface: ChromeUtils.generateQI(["nsIPKCS11ModuleDB"]),
     70 };
     71 
     72 const gMockPromptService = {
     73  alertCallCount: 0,
     74  expectedText: "",
     75  expectedWindow: null,
     76 
     77  alert(parent, dialogTitle, text) {
     78    this.alertCallCount++;
     79    Assert.equal(
     80      parent,
     81      this.expectedWindow,
     82      "alert: Parent should be expected window"
     83    );
     84    Assert.equal(dialogTitle, null, "alert: Title should be null");
     85    Assert.equal(
     86      text,
     87      this.expectedText,
     88      "alert: Actual and expected text should match"
     89    );
     90  },
     91 
     92  QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
     93 };
     94 
     95 var gMockPKCS11CID = MockRegistrar.register(
     96  "@mozilla.org/security/pkcs11moduledb;1",
     97  gMockPKCS11ModuleDB
     98 );
     99 var gMockPromptServiceCID = MockRegistrar.register(
    100  "@mozilla.org/prompter;1",
    101  gMockPromptService
    102 );
    103 
    104 var gMockFilePicker = SpecialPowers.MockFilePicker;
    105 gMockFilePicker.init(window.browsingContext);
    106 
    107 var gTempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
    108 gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
    109 
    110 registerCleanupFunction(() => {
    111  gMockFilePicker.cleanup();
    112  MockRegistrar.unregister(gMockPKCS11CID);
    113  MockRegistrar.unregister(gMockPromptServiceCID);
    114 });
    115 
    116 function resetCallCounts() {
    117  gMockPKCS11ModuleDB.addModuleCallCount = 0;
    118  gMockPromptService.alertCallCount = 0;
    119 }
    120 
    121 /**
    122 * Opens the dialog shown to load a PKCS #11 module.
    123 *
    124 * @returns {Promise}
    125 *          A promise that resolves when the dialog has finished loading, with
    126 *          the window of the opened dialog.
    127 */
    128 function openLoadModuleDialog() {
    129  let win = window.openDialog(
    130    "chrome://pippki/content/load_device.xhtml",
    131    "",
    132    ""
    133  );
    134  return new Promise(resolve => {
    135    win.addEventListener(
    136      "load",
    137      function () {
    138        executeSoon(() => resolve(win));
    139      },
    140      { once: true }
    141    );
    142  });
    143 }
    144 
    145 /**
    146 * Presses the browse button and simulates interacting with the file picker that
    147 * should be triggered.
    148 *
    149 * @param {window} win
    150 *        The dialog window.
    151 * @param {boolean} cancel
    152 *        If true, the file picker is canceled. If false, gTempFile is chosen in
    153 *        the file picker and the file picker is accepted.
    154 */
    155 async function browseToTempFile(win, cancel) {
    156  gMockFilePicker.showCallback = () => {
    157    gMockFilePicker.setFiles([gTempFile]);
    158 
    159    if (cancel) {
    160      info("MockFilePicker returning cancel");
    161      return Ci.nsIFilePicker.returnCancel;
    162    }
    163 
    164    info("MockFilePicker returning OK");
    165    return Ci.nsIFilePicker.returnOK;
    166  };
    167 
    168  info("Pressing browse button");
    169  win.document.getElementById("browse").doCommand();
    170  await TestUtils.topicObserved("LoadPKCS11Module:FilePickHandled");
    171 }
    172 
    173 add_task(async function testBrowseButton() {
    174  let win = await openLoadModuleDialog();
    175  let pathBox = win.document.getElementById("device_path");
    176  let originalPathBoxValue = "expected path if picker is canceled";
    177  pathBox.value = originalPathBoxValue;
    178 
    179  // Test what happens if the file picker is canceled.
    180  await browseToTempFile(win, true);
    181  Assert.equal(
    182    pathBox.value,
    183    originalPathBoxValue,
    184    "Path shown should be unchanged due to canceled picker"
    185  );
    186 
    187  // Test what happens if the file picker is not canceled.
    188  await browseToTempFile(win, false);
    189  Assert.equal(
    190    pathBox.value,
    191    gTempFile.path,
    192    "Path shown should be same as the one chosen in the file picker"
    193  );
    194 
    195  await BrowserTestUtils.closeWindow(win);
    196 });
    197 
    198 function testAddModuleHelper(win, throwOnAddModule) {
    199  resetCallCounts();
    200  gMockPKCS11ModuleDB.expectedLibPath = gTempFile.path;
    201  gMockPKCS11ModuleDB.expectedModuleName = "test module";
    202  gMockPKCS11ModuleDB.throwOnAddModule = throwOnAddModule;
    203 
    204  win.document.getElementById("device_name").value =
    205    gMockPKCS11ModuleDB.expectedModuleName;
    206  win.document.getElementById("device_path").value =
    207    gMockPKCS11ModuleDB.expectedLibPath;
    208 
    209  info("Accepting dialog");
    210  win.document.getElementById("loaddevice").acceptDialog();
    211 }
    212 
    213 add_task(async function testAddModuleSuccess() {
    214  let win = await openLoadModuleDialog();
    215 
    216  testAddModuleHelper(win, false);
    217  await BrowserTestUtils.windowClosed(win);
    218 
    219  Assert.equal(
    220    gMockPKCS11ModuleDB.addModuleCallCount,
    221    1,
    222    "addModule() should have been called once"
    223  );
    224  Assert.equal(
    225    gMockPromptService.alertCallCount,
    226    0,
    227    "alert() should never have been called"
    228  );
    229 });
    230 
    231 add_task(async function testAddModuleFailure() {
    232  let win = await openLoadModuleDialog();
    233  gMockPromptService.expectedText = "Unable to add module";
    234  gMockPromptService.expectedWindow = win;
    235 
    236  // The exception we throw in addModule is first reported as an uncaught
    237  // exception by XPConnect before an exception is propagated to the actual
    238  // caller.
    239  expectUncaughtException(true);
    240 
    241  testAddModuleHelper(win, true);
    242  expectUncaughtException(false);
    243  // If adding a module fails, the dialog will not close. As such, we have to
    244  // close the window ourselves.
    245  await BrowserTestUtils.closeWindow(win);
    246 
    247  Assert.equal(
    248    gMockPKCS11ModuleDB.addModuleCallCount,
    249    1,
    250    "addModule() should have been called once"
    251  );
    252  Assert.equal(
    253    gMockPromptService.alertCallCount,
    254    1,
    255    "alert() should have been called once"
    256  );
    257 });
    258 
    259 add_task(async function testCancel() {
    260  let win = await openLoadModuleDialog();
    261  resetCallCounts();
    262 
    263  info("Canceling dialog");
    264  win.document.getElementById("loaddevice").cancelDialog();
    265 
    266  Assert.equal(
    267    gMockPKCS11ModuleDB.addModuleCallCount,
    268    0,
    269    "addModule() should never have been called"
    270  );
    271  Assert.equal(
    272    gMockPromptService.alertCallCount,
    273    0,
    274    "alert() should never have been called"
    275  );
    276 
    277  await BrowserTestUtils.windowClosed(win);
    278 });
    279 
    280 async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) {
    281  let win = await openLoadModuleDialog();
    282  resetCallCounts();
    283 
    284  info(`Setting Module Name to '${moduleName}'`);
    285  let moduleNameBox = win.document.getElementById("device_name");
    286  moduleNameBox.value = moduleName;
    287  // this makes this not a great test, but it's the easiest way to simulate this
    288  moduleNameBox.dispatchEvent(new Event("change", { bubbles: true }));
    289 
    290  let dialogNode = win.document.querySelector("dialog");
    291  Assert.equal(
    292    dialogNode.hasAttribute("buttondisabledaccept"),
    293    acceptButtonShouldBeDisabled,
    294    `dialog accept button should ${
    295      acceptButtonShouldBeDisabled ? "" : "not "
    296    }be disabled`
    297  );
    298 
    299  return BrowserTestUtils.closeWindow(win);
    300 }
    301 
    302 add_task(async function testEmptyModuleName() {
    303  await testModuleNameHelper("", true);
    304 });
    305 
    306 add_task(async function testReservedModuleName() {
    307  await testModuleNameHelper("Root Certs", true);
    308 });
    309 
    310 add_task(async function testAcceptableModuleName() {
    311  await testModuleNameHelper("Some Module Name", false);
    312 });