Commit 37a3ea49 authored by Mike Kaply's avatar Mike Kaply
Browse files

Bug 1640931 - Allow policy to force addons in private browsing. a=dmeehan

parent e942f3ef
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -710,6 +710,9 @@
            },
            "temporarily_allow_weak_signatures": {
              "type": "boolean"
            },
            "private_browsing": {
              "type": "boolean"
            }
          }
        }
+57 −0
Original line number Diff line number Diff line
@@ -2,6 +2,10 @@
 * http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const { ExtensionPermissions } = ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionPermissions.sys.mjs"
);

const ADDON_ID = "policytest@mozilla.com";
const BASE_URL =
  "http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser";
@@ -18,6 +22,59 @@ async function isExtensionLockedAndUpdateDisabled(win, addonID) {
  is(updateRow.hidden, true, "Update row should be hidden");
}

add_task(async function test_addon_private_browser_access_locked() {
  async function installWithExtensionSettings(extensionSettings = {}) {
    let installPromise = waitForAddonInstall(ADDON_ID);
    await setupPolicyEngineWithJson({
      policies: {
        ExtensionSettings: {
          "policytest@mozilla.com": {
            install_url: `${BASE_URL}/policytest_v0.1.xpi`,
            installation_mode: "force_installed",
            updates_disabled: true,
            ...extensionSettings,
          },
        },
      },
    });
    await installPromise;
    let addon = await AddonManager.getAddonByID(ADDON_ID);
    isnot(addon, null, "Addon not installed.");
    is(addon.version, "0.1", "Addon version is correct");
    return addon;
  }

  let addon = await installWithExtensionSettings();
  is(
    Boolean(
      addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
    ),
    true,
    "Addon should be able to change private browsing setting (not set in policy)."
  );
  await addon.uninstall();

  addon = await installWithExtensionSettings({ private_browsing: true });
  is(
    Boolean(
      addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
    ),
    false,
    "Addon should NOT be able to change private browsing setting (set to true in policy)."
  );
  await addon.uninstall();

  addon = await installWithExtensionSettings({ private_browsing: false });
  is(
    Boolean(
      addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
    ),
    false,
    "Addon should NOT be able to change private browsing setting (set to false in policy)."
  );
  await addon.uninstall();
});

add_task(async function test_addon_install() {
  let installPromise = waitForAddonInstall(ADDON_ID);
  await setupPolicyEngineWithJson({
+7 −0
Original line number Diff line number Diff line
"use strict";

module.exports = {
  env: {
    webextensions: true,
  },
};
+102 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ const { ExtensionTestUtils } = ChromeUtils.importESModule(
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.appInfo = getAppInfo();
AddonTestUtils.usePrivilegedSignatures = false;
ExtensionTestUtils.init(this);

const server = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
@@ -555,7 +556,7 @@ add_task(async function test_policy_installed_only_addon() {
  // Setting back the extension settings with installation_mode set to force_installed
  // will install the extension again, and so we need to wait for that and uninstall
  // it first (otherwise the addon may endup being installed when the test task is
  // completed and trigger an intermittent failre).
  // completed and trigger an intermittent failure).
  installPromise = waitForAddonInstall(policyOnlyID);
  await setupPolicyEngineWithJson({
    policies: {
@@ -613,3 +614,103 @@ add_task(async function test_policy_installed_only_addon() {
    "Got the expect error logged on installing enterprise only extension"
  );
});

add_task(async function test_private_browsing() {
  async function assertPrivateBrowsingAccess({
    addonId,
    extensionSettings,
    extensionManifest = {},
    expectedPrivateBrowsingAccess,
    message,
  }) {
    await setupPolicyEngineWithJson({
      policies: {
        ExtensionSettings: {
          [addonId]: { ...extensionSettings },
        },
      },
    });

    let ext = ExtensionTestUtils.loadExtension({
      manifest: {
        browser_specific_settings: {
          gecko: { id: addonId },
        },
        ...extensionManifest,
      },
      useAddonManager: "temporary",
      background() {
        browser.test.onMessage.addListener(async msg => {
          switch (msg) {
            case "checkPrivateBrowsing": {
              let isAllowed =
                await browser.extension.isAllowedIncognitoAccess();
              browser.test.sendMessage("privateBrowsing", isAllowed);
              break;
            }
          }
        });
      },
    });

    await ext.startup();

    ext.sendMessage("checkPrivateBrowsing");
    let isAllowedIncognitoAccess = await ext.awaitMessage("privateBrowsing");
    Assert.equal(
      isAllowedIncognitoAccess,
      expectedPrivateBrowsingAccess,
      message
    );

    let addon = await AddonManager.getAddonByID(ext.id);
    // Sanity check (to ensure the test extension used in this test are not privileged).
    ok(!addon.isPrivileged, "Addon should not be privileged");

    let expectLocked = typeof extensionSettings?.private_browsing === "boolean";
    Assert.equal(
      !(
        addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
      ),
      expectLocked,
      `Addon should ${expectLocked ? "NOT" : ""} be able to change private browsing setting.`
    );

    await ext.unload();
  }

  await assertPrivateBrowsingAccess({
    addonId: "privatebrowsing-granted-nonprivileged@test",
    extensionSettings: { private_browsing: true },
    expectedPrivateBrowsingAccess: true,
    message:
      "Should have access to private browsing (set to true in policy extension setting)",
  });

  await assertPrivateBrowsingAccess({
    addonId: "privatebrowsing-revoked-nonprivileged@test",
    extensionSettings: { private_browsing: false },
    expectedPrivateBrowsingAccess: false,
    message:
      "Should NOT have access to private browsing (set to false in policy extension setting)",
  });

  await assertPrivateBrowsingAccess({
    addonId: "privatebrowsing-nosetting-nonprivileged@test",
    extensionSettings: {},
    expectedPrivateBrowsingAccess: false,
    message:
      "Should NOT have access to private browsing (NOT set in policy extension setting)",
  });

  await assertPrivateBrowsingAccess({
    addonId: "privatebrowsing-notallowed-nonprivileged@test",
    extensionSettings: { private_browsing: true },
    extensionManifest: {
      incognito: "not_allowed",
    },
    expectedPrivateBrowsingAccess: false,
    message:
      "incognito 'not_allowed' extensions should NOT have access to private browser",
  });
});
+28 −1
Original line number Diff line number Diff line
@@ -3738,7 +3738,8 @@ export class Extension extends ExtensionData {
    // We automatically add permissions to system/built-in extensions.
    // Extensions expliticy stating not_allowed will never get permission.
    let isAllowed = this.permissions.has(PRIVATE_ALLOWED_PERMISSION);
    if (this.manifest.incognito === "not_allowed") {
    const hasIncognitoNotAllowed = this.manifest.incognito === "not_allowed";
    if (hasIncognitoNotAllowed) {
      // If an extension previously had permission, but upgrades/downgrades to
      // a version that specifies "not_allowed" in manifest, remove the
      // permission.
@@ -3764,6 +3765,32 @@ export class Extension extends ExtensionData {
      this.permissions.add(PRIVATE_ALLOWED_PERMISSION);
    }

    // On builds where Enterprise Policies are supported, grant or revoke
    // the private browsing access for extensions that are not app provided
    // (system and builtin add-ons) or hidden.
    if (
      Services.policies &&
      !this.isAppProvided &&
      !this.isHidden &&
      !hasIncognitoNotAllowed &&
      this.type === "extension"
    ) {
      const settings = Services.policies.getExtensionSettings(this.id);
      if (settings?.private_browsing) {
        lazy.ExtensionPermissions.add(this.id, {
          permissions: [PRIVATE_ALLOWED_PERMISSION],
          origins: [],
        });
        this.permissions.add(PRIVATE_ALLOWED_PERMISSION);
      } else if (settings?.private_browsing === false) {
        lazy.ExtensionPermissions.remove(this.id, {
          permissions: [PRIVATE_ALLOWED_PERMISSION],
          origins: [],
        });
        this.permissions.delete(PRIVATE_ALLOWED_PERMISSION);
      }
    }

    // We only want to update the SVG_CONTEXT_PROPERTIES_PERMISSION during
    // install and upgrade/downgrade startups.
    if (INSTALL_AND_UPDATE_STARTUP_REASONS.has(this.startupReason)) {
Loading