test_extensionsettings.js (19894B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 "use strict"; 4 5 const { AddonTestUtils } = ChromeUtils.importESModule( 6 "resource://testing-common/AddonTestUtils.sys.mjs" 7 ); 8 const { AddonManager } = ChromeUtils.importESModule( 9 "resource://gre/modules/AddonManager.sys.mjs" 10 ); 11 12 AddonTestUtils.init(this); 13 AddonTestUtils.overrideCertDB(); 14 AddonTestUtils.appInfo = getAppInfo(); 15 AddonTestUtils.usePrivilegedSignatures = false; 16 ExtensionTestUtils.init(this); 17 18 const server = AddonTestUtils.createHttpServer({ hosts: ["example.com"] }); 19 const BASE_URL = `http://example.com/data`; 20 21 let addonID = "policytest2@mozilla.com"; 22 let themeID = "policytheme@mozilla.com"; 23 let policyOnlyID = "policy_installed_only@mozilla.com"; 24 25 let fileURL; 26 27 function waitForAddonInstall(addonId) { 28 return new Promise(resolve => { 29 let listener = { 30 onInstallEnded(install, addon) { 31 if (addon.id == addonId) { 32 AddonManager.removeInstallListener(listener); 33 resolve(); 34 } 35 }, 36 onDownloadFailed() { 37 AddonManager.removeInstallListener(listener); 38 resolve(); 39 }, 40 onInstallFailed() { 41 AddonManager.removeInstallListener(listener); 42 resolve(); 43 }, 44 }; 45 AddonManager.addInstallListener(listener); 46 }); 47 } 48 49 add_setup(async function setup() { 50 await AddonTestUtils.promiseStartupManager(); 51 52 let webExtensionFile = AddonTestUtils.createTempWebExtensionFile({ 53 manifest: { 54 browser_specific_settings: { 55 gecko: { 56 id: addonID, 57 }, 58 }, 59 }, 60 }); 61 server.registerFile("/data/policy_test.xpi", webExtensionFile); 62 fileURL = Services.io 63 .newFileURI(webExtensionFile) 64 .QueryInterface(Ci.nsIFileURL); 65 66 let policyOnlyExtensionFile = AddonTestUtils.createTempWebExtensionFile({ 67 manifest: { 68 name: "Enterprise Policy Only Test Extension", 69 browser_specific_settings: { 70 gecko: { 71 id: policyOnlyID, 72 admin_install_only: true, 73 }, 74 }, 75 }, 76 }); 77 server.registerFile( 78 "/data/policy_installed_only.xpi", 79 policyOnlyExtensionFile 80 ); 81 82 server.registerFile( 83 "/data/amosigned-sha1only.xpi", 84 do_get_file("amosigned-sha1only.xpi") 85 ); 86 }); 87 88 add_task(async function test_extensionsettings() { 89 await setupPolicyEngineWithJson({ 90 policies: { 91 ExtensionSettings: { 92 "extension1@mozilla.com": { 93 blocked_install_message: "Extension1 error message.", 94 }, 95 "*": { 96 blocked_install_message: "Generic error message.", 97 }, 98 }, 99 }, 100 }); 101 102 let extensionSettings = Services.policies.getExtensionSettings( 103 "extension1@mozilla.com" 104 ); 105 equal( 106 extensionSettings.blocked_install_message, 107 "Extension1 error message.", 108 "Should have extension specific message." 109 ); 110 extensionSettings = Services.policies.getExtensionSettings( 111 "extension2@mozilla.com" 112 ); 113 equal( 114 extensionSettings.blocked_install_message, 115 "Generic error message.", 116 "Should have generic message." 117 ); 118 }); 119 120 add_task(async function test_addon_blocked() { 121 await setupPolicyEngineWithJson({ 122 policies: { 123 ExtensionSettings: { 124 "policytest2@mozilla.com": { 125 installation_mode: "blocked", 126 }, 127 }, 128 }, 129 }); 130 131 let install = await AddonManager.getInstallForURL( 132 BASE_URL + "/policy_test.xpi" 133 ); 134 await install.install(); 135 notEqual(install.addon, null, "Addon should not be null"); 136 equal(install.addon.appDisabled, true, "Addon should be disabled"); 137 await install.addon.uninstall(); 138 }); 139 140 add_task(async function test_addon_allowed() { 141 await setupPolicyEngineWithJson({ 142 policies: { 143 ExtensionSettings: { 144 "policytest2@mozilla.com": { 145 installation_mode: "allowed", 146 }, 147 "*": { 148 installation_mode: "blocked", 149 }, 150 }, 151 }, 152 }); 153 154 let install = await AddonManager.getInstallForURL( 155 BASE_URL + "/policy_test.xpi" 156 ); 157 await install.install(); 158 notEqual(install.addon, null, "Addon should not be null"); 159 await assertManagementAPIInstallType(install.addon.id, "normal"); 160 equal(install.addon.appDisabled, false, "Addon should not be disabled"); 161 equal( 162 install.addon.isInstalledByEnterprisePolicy, 163 false, 164 "Addon should NOT be marked as installed by enterprise policy" 165 ); 166 167 await install.addon.uninstall(); 168 }); 169 170 add_task(async function test_addon_uninstalled() { 171 let install = await AddonManager.getInstallForURL( 172 BASE_URL + "/policy_test.xpi" 173 ); 174 await install.install(); 175 notEqual(install.addon, null, "Addon should not be null"); 176 177 await Promise.all([ 178 AddonTestUtils.promiseAddonEvent("onUninstalled"), 179 setupPolicyEngineWithJson({ 180 policies: { 181 ExtensionSettings: { 182 "*": { 183 installation_mode: "blocked", 184 }, 185 }, 186 }, 187 }), 188 ]); 189 let addon = await AddonManager.getAddonByID(addonID); 190 equal(addon, null, "Addon should be null"); 191 }); 192 193 add_task(async function test_addon_forceinstalled() { 194 await Promise.all([ 195 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 196 setupPolicyEngineWithJson({ 197 policies: { 198 ExtensionSettings: { 199 "policytest2@mozilla.com": { 200 installation_mode: "force_installed", 201 install_url: BASE_URL + "/policy_test.xpi", 202 }, 203 }, 204 }, 205 }), 206 ]); 207 let addon = await AddonManager.getAddonByID(addonID); 208 notEqual(addon, null, "Addon should not be null"); 209 equal(addon.appDisabled, false, "Addon should not be disabled"); 210 equal( 211 addon.permissions & AddonManager.PERM_CAN_UNINSTALL, 212 0, 213 "Addon should not be able to be uninstalled." 214 ); 215 equal( 216 addon.permissions & AddonManager.PERM_CAN_DISABLE, 217 0, 218 "Addon should not be able to be disabled." 219 ); 220 await assertManagementAPIInstallType(addon.id, "admin"); 221 222 await addon.uninstall(); 223 }); 224 225 add_task(async function test_addon_normalinstalled() { 226 await Promise.all([ 227 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 228 setupPolicyEngineWithJson({ 229 policies: { 230 ExtensionSettings: { 231 "policytest2@mozilla.com": { 232 installation_mode: "normal_installed", 233 install_url: BASE_URL + "/policy_test.xpi", 234 }, 235 }, 236 }, 237 }), 238 ]); 239 let addon = await AddonManager.getAddonByID(addonID); 240 notEqual(addon, null, "Addon should not be null"); 241 equal(addon.appDisabled, false, "Addon should not be disabled"); 242 equal( 243 addon.permissions & AddonManager.PERM_CAN_UNINSTALL, 244 0, 245 "Addon should not be able to be uninstalled." 246 ); 247 notEqual( 248 addon.permissions & AddonManager.PERM_CAN_DISABLE, 249 0, 250 "Addon should be able to be disabled." 251 ); 252 await assertManagementAPIInstallType(addon.id, "admin"); 253 254 await addon.uninstall(); 255 }); 256 257 add_task(async function test_extensionsettings_string() { 258 await setupPolicyEngineWithJson({ 259 policies: { 260 ExtensionSettings: '{"*": {"installation_mode": "blocked"}}', 261 }, 262 }); 263 264 let extensionSettings = Services.policies.getExtensionSettings("*"); 265 equal(extensionSettings.installation_mode, "blocked"); 266 }); 267 268 add_task(async function test_extensionsettings_string() { 269 let restrictedDomains = Services.prefs.getCharPref( 270 "extensions.webextensions.restrictedDomains" 271 ); 272 await setupPolicyEngineWithJson({ 273 policies: { 274 ExtensionSettings: 275 '{"*": {"restricted_domains": ["example.com","example.org"]}}', 276 }, 277 }); 278 279 let newRestrictedDomains = Services.prefs.getCharPref( 280 "extensions.webextensions.restrictedDomains" 281 ); 282 equal(newRestrictedDomains, restrictedDomains + ",example.com,example.org"); 283 }); 284 285 add_task(async function test_theme() { 286 let themeFile = AddonTestUtils.createTempWebExtensionFile({ 287 manifest: { 288 browser_specific_settings: { 289 gecko: { 290 id: themeID, 291 }, 292 }, 293 theme: {}, 294 }, 295 }); 296 297 server.registerFile("/data/policy_theme.xpi", themeFile); 298 299 await Promise.all([ 300 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 301 setupPolicyEngineWithJson({ 302 policies: { 303 ExtensionSettings: { 304 "policytheme@mozilla.com": { 305 installation_mode: "normal_installed", 306 install_url: BASE_URL + "/policy_theme.xpi", 307 }, 308 }, 309 }, 310 }), 311 ]); 312 let currentTheme = Services.prefs.getCharPref("extensions.activeThemeID"); 313 equal(currentTheme, themeID, "Theme should be active"); 314 let addon = await AddonManager.getAddonByID(themeID); 315 await addon.uninstall(); 316 }); 317 318 add_task(async function test_addon_normalinstalled_file() { 319 await Promise.all([ 320 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 321 setupPolicyEngineWithJson({ 322 policies: { 323 ExtensionSettings: { 324 "policytest2@mozilla.com": { 325 installation_mode: "normal_installed", 326 install_url: fileURL.spec, 327 }, 328 }, 329 }, 330 }), 331 ]); 332 let addon = await AddonManager.getAddonByID(addonID); 333 notEqual(addon, null, "Addon should not be null"); 334 equal(addon.appDisabled, false, "Addon should not be disabled"); 335 equal( 336 addon.permissions & AddonManager.PERM_CAN_UNINSTALL, 337 0, 338 "Addon should not be able to be uninstalled." 339 ); 340 notEqual( 341 addon.permissions & AddonManager.PERM_CAN_DISABLE, 342 0, 343 "Addon should be able to be disabled." 344 ); 345 await assertManagementAPIInstallType(addon.id, "admin"); 346 347 await addon.uninstall(); 348 }); 349 350 add_task(async function test_allow_weak_signatures() { 351 // Make sure weak signatures are restricted. 352 const resetWeakSignaturePref = 353 AddonTestUtils.setWeakSignatureInstallAllowed(false); 354 355 const id = "amosigned-xpi@tests.mozilla.org"; 356 const perAddonSettings = { 357 installation_mode: "normal_installed", 358 install_url: BASE_URL + "/amosigned-sha1only.xpi", 359 }; 360 361 info( 362 "Sanity check: expect install to fail if not allowed through enterprise policy settings" 363 ); 364 await Promise.all([ 365 AddonTestUtils.promiseInstallEvent("onDownloadFailed"), 366 setupPolicyEngineWithJson({ 367 policies: { 368 ExtensionSettings: { 369 [id]: { ...perAddonSettings }, 370 }, 371 }, 372 }), 373 ]); 374 let addon = await AddonManager.getAddonByID(id); 375 equal(addon, null, "Add-on not installed"); 376 377 info( 378 "Expect install to be allowed through per-addon enterprise policy settings" 379 ); 380 await Promise.all([ 381 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 382 setupPolicyEngineWithJson({ 383 policies: { 384 ExtensionSettings: { 385 [id]: { 386 ...perAddonSettings, 387 temporarily_allow_weak_signatures: true, 388 }, 389 }, 390 }, 391 }), 392 ]); 393 addon = await AddonManager.getAddonByID(id); 394 notEqual(addon, null, "Add-on not installed"); 395 await addon.uninstall(); 396 397 info( 398 "Expect install to be allowed through global enterprise policy settings" 399 ); 400 await Promise.all([ 401 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 402 setupPolicyEngineWithJson({ 403 policies: { 404 ExtensionSettings: { 405 "*": { temporarily_allow_weak_signatures: true }, 406 [id]: { ...perAddonSettings }, 407 }, 408 }, 409 }), 410 ]); 411 addon = await AddonManager.getAddonByID(id); 412 notEqual(addon, null, "Add-on installed"); 413 await addon.uninstall(); 414 415 info( 416 "Expect install to fail if allowed globally but disallowed by per-addon settings" 417 ); 418 await Promise.all([ 419 AddonTestUtils.promiseInstallEvent("onDownloadFailed"), 420 setupPolicyEngineWithJson({ 421 policies: { 422 ExtensionSettings: { 423 "*": { temporarily_allow_weak_signatures: true }, 424 [id]: { 425 ...perAddonSettings, 426 temporarily_allow_weak_signatures: false, 427 }, 428 }, 429 }, 430 }), 431 ]); 432 addon = await AddonManager.getAddonByID(id); 433 equal(addon, null, "Add-on not installed"); 434 435 info( 436 "Expect install to be allowed through per addon setting when globally disallowed" 437 ); 438 await Promise.all([ 439 AddonTestUtils.promiseInstallEvent("onInstallEnded"), 440 setupPolicyEngineWithJson({ 441 policies: { 442 ExtensionSettings: { 443 "*": { temporarily_allow_weak_signatures: false }, 444 [id]: { 445 ...perAddonSettings, 446 temporarily_allow_weak_signatures: true, 447 }, 448 }, 449 }, 450 }), 451 ]); 452 addon = await AddonManager.getAddonByID(id); 453 notEqual(addon, null, "Add-on installed"); 454 await addon.uninstall(); 455 456 resetWeakSignaturePref(); 457 }); 458 459 add_task(async function test_policy_installed_only_addon() { 460 const cleanupTestAddon = async () => { 461 const addon = await AddonManager.getAddonByID(policyOnlyID); 462 if (addon) { 463 await addon.uninstall(); 464 } 465 }; 466 registerCleanupFunction(cleanupTestAddon); 467 468 info( 469 "Expect the test addon to install successfully when installed by the policy" 470 ); 471 let installPromise = waitForAddonInstall(policyOnlyID); 472 await setupPolicyEngineWithJson({ 473 policies: { 474 ExtensionSettings: { 475 [policyOnlyID]: { 476 install_url: `${BASE_URL}/policy_installed_only.xpi`, 477 installation_mode: "force_installed", 478 updates_disabled: true, 479 }, 480 }, 481 }, 482 }); 483 await installPromise; 484 let addon = await AddonManager.getAddonByID(policyOnlyID); 485 Assert.notEqual(addon, null, "Addon expected to be installed successfully."); 486 Assert.deepEqual( 487 addon.installTelemetryInfo, 488 { source: "enterprise-policy" }, 489 "Got the expected addon.installTelemetryInfo" 490 ); 491 492 info( 493 "Remove the ExtensionSettings and verify the addon becomes appDisabled on XPIDatabase.verifySignatures calls" 494 ); 495 await setupPolicyEngineWithJson({ 496 policies: { 497 ExtensionSettings: { 498 // NOTE: an empty ExtensionSettings config would not be reflected 499 // by data returned by Services.policy.getExtensionSettings calls, 500 // on the contrary setting new policy data with an unrelated addon-id 501 // forces the ExtensionSettings data to be refreshed. 502 "someother-addon@test": { updates_disabled: true }, 503 }, 504 }, 505 }); 506 507 { 508 const { XPIExports } = ChromeUtils.importESModule( 509 "resource://gre/modules/addons/XPIExports.sys.mjs" 510 ); 511 await XPIExports.XPIDatabase.verifySignatures(); 512 } 513 514 addon = await AddonManager.getAddonByID(policyOnlyID); 515 Assert.notEqual(addon, null, "Addon expected to still be installed"); 516 Assert.equal(addon.appDisabled, true, "Expect the addon to be appDisabled"); 517 518 await cleanupTestAddon(); 519 addon = await AddonManager.getAddonByID(policyOnlyID); 520 Assert.equal(addon, null, "Addon expected to not be installed anymore"); 521 522 info( 523 "Expect the test addon to NOT install successfully when not installed by the policy" 524 ); 525 // Setting back the extension settings with installation_mode set to force_installed 526 // will install the extension again, and so we need to wait for that and uninstall 527 // it first (otherwise the addon may endup being installed when the test task is 528 // completed and trigger an intermittent failure). 529 installPromise = waitForAddonInstall(policyOnlyID); 530 await setupPolicyEngineWithJson({ 531 policies: { 532 ExtensionSettings: { 533 [policyOnlyID]: { 534 install_url: `${BASE_URL}/policy_installed_only.xpi`, 535 installation_mode: "force_installed", 536 updates_disabled: true, 537 }, 538 }, 539 }, 540 }); 541 await installPromise; 542 await cleanupTestAddon(); 543 544 await setupPolicyEngineWithJson({ 545 policies: { 546 ExtensionSettings: { 547 // NOTE: an empty ExtensionSettings config would not be reflected 548 // by data returned by Services.policy.getExtensionSettings calls, 549 // on the contrary setting new policy data with an unrelated addon-id 550 // forces the ExtensionSettings data to be refreshed. 551 "someother-addon@test": { updates_disabled: true }, 552 }, 553 }, 554 }); 555 556 const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { 557 installPromise = waitForAddonInstall(policyOnlyID); 558 const install = await AddonManager.getInstallForURL( 559 `${BASE_URL}/policy_installed_only.xpi`, 560 { telemetryInfo: { source: "not-enterprise-policy" } } 561 ); 562 await Assert.rejects( 563 install.install(), 564 /Install failed/, 565 "Expect the install method to reject" 566 ); 567 await installPromise; 568 569 addon = await AddonManager.getAddonByID(policyOnlyID); 570 Assert.equal(addon, null, "Addon expected to not be installed"); 571 }); 572 573 AddonTestUtils.checkMessages( 574 messages, 575 { 576 expected: [ 577 { 578 message: 579 /This addon can only be installed through Enterprise Policies/, 580 }, 581 ], 582 }, 583 "Got the expect error logged on installing enterprise only extension" 584 ); 585 }); 586 587 add_task(async function test_private_browsing() { 588 async function assertPrivateBrowsingAccess({ 589 addonId, 590 extensionSettings, 591 extensionManifest = {}, 592 expectedPrivateBrowsingAccess, 593 message, 594 }) { 595 await setupPolicyEngineWithJson({ 596 policies: { 597 ExtensionSettings: { 598 [addonId]: { ...extensionSettings }, 599 }, 600 }, 601 }); 602 603 let ext = ExtensionTestUtils.loadExtension({ 604 manifest: { 605 browser_specific_settings: { 606 gecko: { id: addonId }, 607 }, 608 ...extensionManifest, 609 }, 610 useAddonManager: "temporary", 611 background() { 612 browser.test.onMessage.addListener(async msg => { 613 switch (msg) { 614 case "checkPrivateBrowsing": { 615 let isAllowed = 616 await browser.extension.isAllowedIncognitoAccess(); 617 browser.test.sendMessage("privateBrowsing", isAllowed); 618 break; 619 } 620 } 621 }); 622 }, 623 }); 624 625 await ext.startup(); 626 627 ext.sendMessage("checkPrivateBrowsing"); 628 let isAllowedIncognitoAccess = await ext.awaitMessage("privateBrowsing"); 629 Assert.equal( 630 isAllowedIncognitoAccess, 631 expectedPrivateBrowsingAccess, 632 message 633 ); 634 635 let addon = await AddonManager.getAddonByID(ext.id); 636 // Sanity check (to ensure the test extension used in this test are not privileged). 637 ok(!addon.isPrivileged, "Addon should not be privileged"); 638 639 let expectLocked = typeof extensionSettings?.private_browsing === "boolean"; 640 Assert.equal( 641 !( 642 addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS 643 ), 644 expectLocked, 645 `Addon should ${expectLocked ? "NOT" : ""} be able to change private browsing setting.` 646 ); 647 648 await ext.unload(); 649 } 650 651 await assertPrivateBrowsingAccess({ 652 addonId: "privatebrowsing-granted-nonprivileged@test", 653 extensionSettings: { private_browsing: true }, 654 expectedPrivateBrowsingAccess: true, 655 message: 656 "Should have access to private browsing (set to true in policy extension setting)", 657 }); 658 659 await assertPrivateBrowsingAccess({ 660 addonId: "privatebrowsing-revoked-nonprivileged@test", 661 extensionSettings: { private_browsing: false }, 662 expectedPrivateBrowsingAccess: false, 663 message: 664 "Should NOT have access to private browsing (set to false in policy extension setting)", 665 }); 666 667 await assertPrivateBrowsingAccess({ 668 addonId: "privatebrowsing-nosetting-nonprivileged@test", 669 extensionSettings: {}, 670 expectedPrivateBrowsingAccess: false, 671 message: 672 "Should NOT have access to private browsing (NOT set in policy extension setting)", 673 }); 674 675 await assertPrivateBrowsingAccess({ 676 addonId: "privatebrowsing-notallowed-nonprivileged@test", 677 extensionSettings: { private_browsing: true }, 678 extensionManifest: { 679 incognito: "not_allowed", 680 }, 681 expectedPrivateBrowsingAccess: false, 682 message: 683 "incognito 'not_allowed' extensions should NOT have access to private browser", 684 }); 685 });