tor-browser

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

test_SitePermissions.js (16373B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/
      3 */
      4 "use strict";
      5 
      6 const { SitePermissions } = ChromeUtils.importESModule(
      7  "resource:///modules/SitePermissions.sys.mjs"
      8 );
      9 
     10 const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref(
     11  "privacy.resistFingerprinting"
     12 );
     13 const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
     14 
     15 const SPEAKER_SELECTION_ENABLED = Services.prefs.getBoolPref(
     16  "media.setsinkid.enabled"
     17 );
     18 
     19 const LOCAL_NETWORK_ACCESS_ENABLED = Services.prefs.getBoolPref(
     20  "network.lna.blocking"
     21 );
     22 
     23 add_task(async function testPermissionsListing() {
     24  let expectedPermissions = [
     25    "autoplay-media",
     26    "camera",
     27    "cookie",
     28    "desktop-notification",
     29    "focus-tab-by-prompt",
     30    "geo",
     31    "install",
     32    "microphone",
     33    "popup",
     34    "screen",
     35    "shortcuts",
     36    "persistent-storage",
     37    "storage-access",
     38    "xr",
     39    "3rdPartyStorage",
     40    "3rdPartyFrameStorage",
     41    "open-protocol-handler",
     42  ];
     43  if (RESIST_FINGERPRINTING_ENABLED) {
     44    // Canvas permission should be hidden unless privacy.resistFingerprinting
     45    // is true.
     46    expectedPermissions.push("canvas");
     47  }
     48  if (MIDI_ENABLED) {
     49    // Should remove this checking and add it as default after it is fully pref'd-on.
     50    expectedPermissions.push("midi");
     51    expectedPermissions.push("midi-sysex");
     52  }
     53  if (SPEAKER_SELECTION_ENABLED) {
     54    expectedPermissions.push("speaker");
     55  }
     56  if (LOCAL_NETWORK_ACCESS_ENABLED) {
     57    expectedPermissions.push("localhost");
     58    expectedPermissions.push("local-network");
     59  }
     60  Assert.deepEqual(
     61    SitePermissions.listPermissions().sort(),
     62    expectedPermissions.sort(),
     63    "Correct list of all permissions"
     64  );
     65 });
     66 
     67 add_task(async function testGetAllByPrincipal() {
     68  // check that it returns an empty array on an invalid principal
     69  // like a principal with an about URI, which doesn't support site permissions
     70  let wrongPrincipal =
     71    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     72      "about:config"
     73    );
     74  Assert.deepEqual(SitePermissions.getAllByPrincipal(wrongPrincipal), []);
     75 
     76  let principal =
     77    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     78      "https://example.com"
     79    );
     80  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
     81 
     82  SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW);
     83  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
     84    {
     85      id: "camera",
     86      state: SitePermissions.ALLOW,
     87      scope: SitePermissions.SCOPE_PERSISTENT,
     88    },
     89  ]);
     90 
     91  SitePermissions.setForPrincipal(
     92    principal,
     93    "microphone",
     94    SitePermissions.ALLOW,
     95    SitePermissions.SCOPE_SESSION
     96  );
     97 
     98  Services.prefs.setBoolPref("network.lna.blocking", true);
     99 
    100  SitePermissions.setForPrincipal(
    101    principal,
    102    "localhost",
    103    SitePermissions.ALLOW,
    104    SitePermissions.SCOPE_SESSION
    105  );
    106 
    107  SitePermissions.setForPrincipal(
    108    principal,
    109    "local-network",
    110    SitePermissions.ALLOW,
    111    SitePermissions.SCOPE_SESSION
    112  );
    113 
    114  SitePermissions.setForPrincipal(
    115    principal,
    116    "desktop-notification",
    117    SitePermissions.BLOCK
    118  );
    119 
    120  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
    121    {
    122      id: "camera",
    123      state: SitePermissions.ALLOW,
    124      scope: SitePermissions.SCOPE_PERSISTENT,
    125    },
    126    {
    127      id: "microphone",
    128      state: SitePermissions.ALLOW,
    129      scope: SitePermissions.SCOPE_SESSION,
    130    },
    131    {
    132      id: "localhost",
    133      state: SitePermissions.ALLOW,
    134      scope: SitePermissions.SCOPE_SESSION,
    135    },
    136    {
    137      id: "local-network",
    138      state: SitePermissions.ALLOW,
    139      scope: SitePermissions.SCOPE_SESSION,
    140    },
    141    {
    142      id: "desktop-notification",
    143      state: SitePermissions.BLOCK,
    144      scope: SitePermissions.SCOPE_PERSISTENT,
    145    },
    146  ]);
    147 
    148  SitePermissions.removeFromPrincipal(principal, "microphone");
    149  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
    150    {
    151      id: "camera",
    152      state: SitePermissions.ALLOW,
    153      scope: SitePermissions.SCOPE_PERSISTENT,
    154    },
    155    {
    156      id: "localhost",
    157      state: SitePermissions.ALLOW,
    158      scope: SitePermissions.SCOPE_SESSION,
    159    },
    160    {
    161      id: "local-network",
    162      state: SitePermissions.ALLOW,
    163      scope: SitePermissions.SCOPE_SESSION,
    164    },
    165    {
    166      id: "desktop-notification",
    167      state: SitePermissions.BLOCK,
    168      scope: SitePermissions.SCOPE_PERSISTENT,
    169    },
    170  ]);
    171 
    172  SitePermissions.removeFromPrincipal(principal, "camera");
    173  SitePermissions.removeFromPrincipal(principal, "desktop-notification");
    174  SitePermissions.removeFromPrincipal(principal, "localhost");
    175  SitePermissions.removeFromPrincipal(principal, "local-network");
    176 
    177  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
    178 
    179  Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0);
    180  SitePermissions.setForPrincipal(
    181    principal,
    182    "shortcuts",
    183    SitePermissions.BLOCK
    184  );
    185 
    186  // Customized preference should have been enabled, but the default should not.
    187  Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0);
    188  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
    189    {
    190      id: "shortcuts",
    191      state: SitePermissions.BLOCK,
    192      scope: SitePermissions.SCOPE_PERSISTENT,
    193    },
    194  ]);
    195 
    196  SitePermissions.removeFromPrincipal(principal, "shortcuts");
    197  Services.prefs.clearUserPref("permissions.default.shortcuts");
    198  Services.prefs.clearUserPref("network.lna.blocking");
    199 });
    200 
    201 add_task(async function testGetAvailableStates() {
    202  Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [
    203    SitePermissions.UNKNOWN,
    204    SitePermissions.ALLOW,
    205    SitePermissions.BLOCK,
    206  ]);
    207 
    208  // Test available states with a default permission set.
    209  Services.prefs.setIntPref(
    210    "permissions.default.camera",
    211    SitePermissions.ALLOW
    212  );
    213  Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [
    214    SitePermissions.PROMPT,
    215    SitePermissions.ALLOW,
    216    SitePermissions.BLOCK,
    217  ]);
    218  Services.prefs.clearUserPref("permissions.default.camera");
    219 
    220  Assert.deepEqual(SitePermissions.getAvailableStates("cookie"), [
    221    SitePermissions.ALLOW,
    222    SitePermissions.ALLOW_COOKIES_FOR_SESSION,
    223    SitePermissions.BLOCK,
    224  ]);
    225 
    226  Assert.deepEqual(SitePermissions.getAvailableStates("popup"), [
    227    SitePermissions.ALLOW,
    228    SitePermissions.BLOCK,
    229  ]);
    230 });
    231 
    232 add_task(async function testExactHostMatch() {
    233  let principal =
    234    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    235      "https://example.com"
    236    );
    237  let subPrincipal =
    238    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    239      "https://test1.example.com"
    240    );
    241 
    242  let exactHostMatched = [
    243    "autoplay-media",
    244    "desktop-notification",
    245    "focus-tab-by-prompt",
    246    "camera",
    247    "localhost",
    248    "local-network",
    249    "microphone",
    250    "screen",
    251    "geo",
    252    "xr",
    253    "persistent-storage",
    254    "open-protocol-handler",
    255  ];
    256  if (RESIST_FINGERPRINTING_ENABLED) {
    257    // Canvas permission should be hidden unless privacy.resistFingerprinting
    258    // is true.
    259    exactHostMatched.push("canvas");
    260  }
    261  if (MIDI_ENABLED) {
    262    // WebMIDI is only pref'd on in nightly.
    263    // Should remove this checking and add it as default after it is fully pref-on.
    264    exactHostMatched.push("midi");
    265    exactHostMatched.push("midi-sysex");
    266  }
    267  if (SPEAKER_SELECTION_ENABLED) {
    268    exactHostMatched.push("speaker");
    269  }
    270  let nonExactHostMatched = [
    271    "cookie",
    272    "popup",
    273    "install",
    274    "shortcuts",
    275    "storage-access",
    276    "3rdPartyStorage",
    277    "3rdPartyFrameStorage",
    278  ];
    279 
    280  let permissions = SitePermissions.listPermissions();
    281  for (let permission of permissions) {
    282    SitePermissions.setForPrincipal(
    283      principal,
    284      permission,
    285      SitePermissions.ALLOW
    286    );
    287 
    288    if (exactHostMatched.includes(permission)) {
    289      // Check that the sub-origin does not inherit the permission from its parent.
    290      Assert.equal(
    291        SitePermissions.getForPrincipal(subPrincipal, permission).state,
    292        SitePermissions.getDefault(permission),
    293        `${permission} should exact-host match`
    294      );
    295    } else if (nonExactHostMatched.includes(permission)) {
    296      // Check that the sub-origin does inherit the permission from its parent.
    297      Assert.equal(
    298        SitePermissions.getForPrincipal(subPrincipal, permission).state,
    299        SitePermissions.ALLOW,
    300        `${permission} should not exact-host match`
    301      );
    302    } else {
    303      Assert.ok(
    304        false,
    305        `Found an unknown permission ${permission} in exact host match test.` +
    306          "Please add new permissions from SitePermissions.sys.mjs to this test."
    307      );
    308    }
    309 
    310    // Check that the permission can be made specific to the sub-origin.
    311    SitePermissions.setForPrincipal(
    312      subPrincipal,
    313      permission,
    314      SitePermissions.PROMPT
    315    );
    316    Assert.equal(
    317      SitePermissions.getForPrincipal(subPrincipal, permission).state,
    318      SitePermissions.PROMPT
    319    );
    320    Assert.equal(
    321      SitePermissions.getForPrincipal(principal, permission).state,
    322      SitePermissions.ALLOW
    323    );
    324 
    325    SitePermissions.removeFromPrincipal(subPrincipal, permission);
    326    SitePermissions.removeFromPrincipal(principal, permission);
    327  }
    328 });
    329 
    330 add_task(async function testDefaultPrefs() {
    331  let principal =
    332    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    333      "https://example.com"
    334    );
    335 
    336  // Check that without a pref the default return value is UNKNOWN.
    337  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    338    state: SitePermissions.UNKNOWN,
    339    scope: SitePermissions.SCOPE_PERSISTENT,
    340  });
    341 
    342  // Check that the default return value changed after setting the pref.
    343  Services.prefs.setIntPref(
    344    "permissions.default.camera",
    345    SitePermissions.BLOCK
    346  );
    347  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    348    state: SitePermissions.BLOCK,
    349    scope: SitePermissions.SCOPE_PERSISTENT,
    350  });
    351 
    352  // Check that other permissions still return UNKNOWN.
    353  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "microphone"), {
    354    state: SitePermissions.UNKNOWN,
    355    scope: SitePermissions.SCOPE_PERSISTENT,
    356  });
    357 
    358  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "localhost"), {
    359    state: SitePermissions.UNKNOWN,
    360    scope: SitePermissions.SCOPE_PERSISTENT,
    361  });
    362 
    363  Assert.deepEqual(
    364    SitePermissions.getForPrincipal(principal, "local-network"),
    365    {
    366      state: SitePermissions.UNKNOWN,
    367      scope: SitePermissions.SCOPE_PERSISTENT,
    368    }
    369  );
    370 
    371  // Check that the default return value changed after changing the pref.
    372  Services.prefs.setIntPref(
    373    "permissions.default.camera",
    374    SitePermissions.ALLOW
    375  );
    376  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    377    state: SitePermissions.ALLOW,
    378    scope: SitePermissions.SCOPE_PERSISTENT,
    379  });
    380 
    381  // Check that the preference is ignored if there is a value.
    382  SitePermissions.setForPrincipal(principal, "camera", SitePermissions.BLOCK);
    383  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    384    state: SitePermissions.BLOCK,
    385    scope: SitePermissions.SCOPE_PERSISTENT,
    386  });
    387 
    388  // The preference should be honored again, after resetting the permissions.
    389  SitePermissions.removeFromPrincipal(principal, "camera");
    390  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    391    state: SitePermissions.ALLOW,
    392    scope: SitePermissions.SCOPE_PERSISTENT,
    393  });
    394 
    395  // Should be UNKNOWN after clearing the pref.
    396  Services.prefs.clearUserPref("permissions.default.camera");
    397  Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
    398    state: SitePermissions.UNKNOWN,
    399    scope: SitePermissions.SCOPE_PERSISTENT,
    400  });
    401 });
    402 
    403 add_task(async function testCanvasPermission() {
    404  let resistFingerprinting = Services.prefs.getBoolPref(
    405    "privacy.resistFingerprinting",
    406    false
    407  );
    408  let principal =
    409    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    410      "https://example.com"
    411    );
    412 
    413  SitePermissions.setForPrincipal(principal, "canvas", SitePermissions.ALLOW);
    414 
    415  // Canvas permission is hidden when privacy.resistFingerprinting is false.
    416  Services.prefs.setBoolPref("privacy.resistFingerprinting", false);
    417  Assert.equal(SitePermissions.listPermissions().indexOf("canvas"), -1);
    418  Assert.equal(
    419    SitePermissions.getAllByPrincipal(principal).filter(
    420      permission => permission.id === "canvas"
    421    ).length,
    422    0
    423  );
    424 
    425  // Canvas permission is visible when privacy.resistFingerprinting is true.
    426  Services.prefs.setBoolPref("privacy.resistFingerprinting", true);
    427  Assert.notEqual(SitePermissions.listPermissions().indexOf("canvas"), -1);
    428  Assert.notEqual(
    429    SitePermissions.getAllByPrincipal(principal).filter(
    430      permission => permission.id === "canvas"
    431    ).length,
    432    0
    433  );
    434 
    435  SitePermissions.removeFromPrincipal(principal, "canvas");
    436  Services.prefs.setBoolPref(
    437    "privacy.resistFingerprinting",
    438    resistFingerprinting
    439  );
    440 });
    441 
    442 add_task(async function testLocalHostPermission() {
    443  let lnaEnabled = Services.prefs.getBoolPref("network.lna.blocking", false);
    444  let principal =
    445    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    446      "https://example.com"
    447    );
    448 
    449  SitePermissions.setForPrincipal(
    450    principal,
    451    "localhost",
    452    SitePermissions.ALLOW
    453  );
    454 
    455  Services.prefs.setBoolPref("network.lna.blocking", false);
    456  Assert.ok(
    457    !SitePermissions.listPermissions().includes("localhost"),
    458    "No 'localhost' permission should be present"
    459  );
    460  Assert.ok(
    461    !SitePermissions.getAllByPrincipal(principal).some(
    462      permission => permission.id === "localhost"
    463    ),
    464    "No 'localhost' permission should be present"
    465  );
    466 
    467  Services.prefs.setBoolPref("network.lna.blocking", true);
    468  Assert.ok(
    469    SitePermissions.listPermissions().includes("localhost"),
    470    "'localhost' should be in listPermissions when blocking is enabled"
    471  );
    472  Assert.ok(
    473    SitePermissions.getAllByPrincipal(principal).some(
    474      permission => permission.id === "localhost"
    475    ),
    476    "'localhost' permission should be present for principal when blocking is enabled"
    477  );
    478 
    479  SitePermissions.removeFromPrincipal(principal, "localhost");
    480  Services.prefs.setBoolPref("network.lna.blocking", lnaEnabled);
    481 });
    482 
    483 add_task(async function testLocalNetworkPermission() {
    484  let lnaEnabled = Services.prefs.getBoolPref("network.lna.blocking", false);
    485  let principal =
    486    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    487      "https://example.com"
    488    );
    489 
    490  SitePermissions.setForPrincipal(
    491    principal,
    492    "local-network",
    493    SitePermissions.ALLOW
    494  );
    495 
    496  Services.prefs.setBoolPref("network.lna.blocking", false);
    497  Assert.ok(
    498    !SitePermissions.listPermissions().includes("local-network"),
    499    "'local-network' should not be in listPermissions when blocking is disabled"
    500  );
    501  Assert.ok(
    502    !SitePermissions.getAllByPrincipal(principal).some(
    503      permission => permission.id === "local-network"
    504    ),
    505    "'local-network' permission should not be present for principal when blocking is disabled"
    506  );
    507 
    508  Services.prefs.setBoolPref("network.lna.blocking", true);
    509  Assert.ok(
    510    SitePermissions.listPermissions().includes("local-network"),
    511    "'local-network' should be in listPermissions when blocking is enabled"
    512  );
    513  Assert.ok(
    514    SitePermissions.getAllByPrincipal(principal).some(
    515      permission => permission.id === "local-network"
    516    ),
    517    "'local-network' permission should be present for principal when blocking is enabled"
    518  );
    519 
    520  SitePermissions.removeFromPrincipal(principal, "local-network");
    521  Services.prefs.setBoolPref("network.lna.blocking", lnaEnabled);
    522 });
    523 
    524 add_task(async function testFilePermissions() {
    525  let principal =
    526    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    527      "file:///example.js"
    528    );
    529  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
    530 
    531  SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW);
    532  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
    533    {
    534      id: "camera",
    535      state: SitePermissions.ALLOW,
    536      scope: SitePermissions.SCOPE_PERSISTENT,
    537    },
    538  ]);
    539  SitePermissions.removeFromPrincipal(principal, "camera");
    540  Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
    541 });