diff options
Diffstat (limited to 'browser/modules/test/unit/test_SitePermissions.js')
-rw-r--r-- | browser/modules/test/unit/test_SitePermissions.js | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/browser/modules/test/unit/test_SitePermissions.js b/browser/modules/test/unit/test_SitePermissions.js new file mode 100644 index 0000000000..e982cf6e99 --- /dev/null +++ b/browser/modules/test/unit/test_SitePermissions.js @@ -0,0 +1,401 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { SitePermissions } = ChromeUtils.importESModule( + "resource:///modules/SitePermissions.sys.mjs" +); + +const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref( + "privacy.resistFingerprinting" +); +const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled"); + +const EXT_PROTOCOL_ENABLED = Services.prefs.getBoolPref( + "security.external_protocol_requires_permission" +); + +const SPEAKER_SELECTION_ENABLED = Services.prefs.getBoolPref( + "media.setsinkid.enabled" +); + +add_task(async function testPermissionsListing() { + let expectedPermissions = [ + "autoplay-media", + "camera", + "cookie", + "desktop-notification", + "focus-tab-by-prompt", + "geo", + "install", + "microphone", + "popup", + "screen", + "shortcuts", + "persistent-storage", + "storage-access", + "xr", + "3rdPartyStorage", + ]; + if (RESIST_FINGERPRINTING_ENABLED) { + // Canvas permission should be hidden unless privacy.resistFingerprinting + // is true. + expectedPermissions.push("canvas"); + } + if (MIDI_ENABLED) { + // Should remove this checking and add it as default after it is fully pref'd-on. + expectedPermissions.push("midi"); + expectedPermissions.push("midi-sysex"); + } + if (EXT_PROTOCOL_ENABLED) { + expectedPermissions.push("open-protocol-handler"); + } + if (SPEAKER_SELECTION_ENABLED) { + expectedPermissions.push("speaker"); + } + Assert.deepEqual( + SitePermissions.listPermissions().sort(), + expectedPermissions.sort(), + "Correct list of all permissions" + ); +}); + +add_task(async function testGetAllByPrincipal() { + // check that it returns an empty array on an invalid principal + // like a principal with an about URI, which doesn't support site permissions + let wrongPrincipal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "about:config" + ); + Assert.deepEqual(SitePermissions.getAllByPrincipal(wrongPrincipal), []); + + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" + ); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []); + + SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [ + { + id: "camera", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + ]); + + SitePermissions.setForPrincipal( + principal, + "microphone", + SitePermissions.ALLOW, + SitePermissions.SCOPE_SESSION + ); + SitePermissions.setForPrincipal( + principal, + "desktop-notification", + SitePermissions.BLOCK + ); + + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [ + { + id: "camera", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + { + id: "microphone", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_SESSION, + }, + { + id: "desktop-notification", + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + ]); + + SitePermissions.removeFromPrincipal(principal, "microphone"); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [ + { + id: "camera", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + { + id: "desktop-notification", + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + ]); + + SitePermissions.removeFromPrincipal(principal, "camera"); + SitePermissions.removeFromPrincipal(principal, "desktop-notification"); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []); + + Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0); + SitePermissions.setForPrincipal( + principal, + "shortcuts", + SitePermissions.BLOCK + ); + + // Customized preference should have been enabled, but the default should not. + Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [ + { + id: "shortcuts", + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + ]); + + SitePermissions.removeFromPrincipal(principal, "shortcuts"); + Services.prefs.clearUserPref("permissions.default.shortcuts"); +}); + +add_task(async function testGetAvailableStates() { + Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [ + SitePermissions.UNKNOWN, + SitePermissions.ALLOW, + SitePermissions.BLOCK, + ]); + + // Test available states with a default permission set. + Services.prefs.setIntPref( + "permissions.default.camera", + SitePermissions.ALLOW + ); + Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [ + SitePermissions.PROMPT, + SitePermissions.ALLOW, + SitePermissions.BLOCK, + ]); + Services.prefs.clearUserPref("permissions.default.camera"); + + Assert.deepEqual(SitePermissions.getAvailableStates("cookie"), [ + SitePermissions.ALLOW, + SitePermissions.ALLOW_COOKIES_FOR_SESSION, + SitePermissions.BLOCK, + ]); + + Assert.deepEqual(SitePermissions.getAvailableStates("popup"), [ + SitePermissions.ALLOW, + SitePermissions.BLOCK, + ]); +}); + +add_task(async function testExactHostMatch() { + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" + ); + let subPrincipal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://test1.example.com" + ); + + let exactHostMatched = [ + "autoplay-media", + "desktop-notification", + "focus-tab-by-prompt", + "camera", + "microphone", + "screen", + "geo", + "xr", + "persistent-storage", + ]; + if (RESIST_FINGERPRINTING_ENABLED) { + // Canvas permission should be hidden unless privacy.resistFingerprinting + // is true. + exactHostMatched.push("canvas"); + } + if (MIDI_ENABLED) { + // WebMIDI is only pref'd on in nightly. + // Should remove this checking and add it as default after it is fully pref-on. + exactHostMatched.push("midi"); + exactHostMatched.push("midi-sysex"); + } + if (EXT_PROTOCOL_ENABLED) { + exactHostMatched.push("open-protocol-handler"); + } + if (SPEAKER_SELECTION_ENABLED) { + exactHostMatched.push("speaker"); + } + let nonExactHostMatched = [ + "cookie", + "popup", + "install", + "shortcuts", + "storage-access", + "3rdPartyStorage", + ]; + + let permissions = SitePermissions.listPermissions(); + for (let permission of permissions) { + SitePermissions.setForPrincipal( + principal, + permission, + SitePermissions.ALLOW + ); + + if (exactHostMatched.includes(permission)) { + // Check that the sub-origin does not inherit the permission from its parent. + Assert.equal( + SitePermissions.getForPrincipal(subPrincipal, permission).state, + SitePermissions.getDefault(permission), + `${permission} should exact-host match` + ); + } else if (nonExactHostMatched.includes(permission)) { + // Check that the sub-origin does inherit the permission from its parent. + Assert.equal( + SitePermissions.getForPrincipal(subPrincipal, permission).state, + SitePermissions.ALLOW, + `${permission} should not exact-host match` + ); + } else { + Assert.ok( + false, + `Found an unknown permission ${permission} in exact host match test.` + + "Please add new permissions from SitePermissions.sys.mjs to this test." + ); + } + + // Check that the permission can be made specific to the sub-origin. + SitePermissions.setForPrincipal( + subPrincipal, + permission, + SitePermissions.PROMPT + ); + Assert.equal( + SitePermissions.getForPrincipal(subPrincipal, permission).state, + SitePermissions.PROMPT + ); + Assert.equal( + SitePermissions.getForPrincipal(principal, permission).state, + SitePermissions.ALLOW + ); + + SitePermissions.removeFromPrincipal(subPrincipal, permission); + SitePermissions.removeFromPrincipal(principal, permission); + } +}); + +add_task(async function testDefaultPrefs() { + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" + ); + + // Check that without a pref the default return value is UNKNOWN. + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // Check that the default return value changed after setting the pref. + Services.prefs.setIntPref( + "permissions.default.camera", + SitePermissions.BLOCK + ); + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // Check that other permissions still return UNKNOWN. + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "microphone"), { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // Check that the default return value changed after changing the pref. + Services.prefs.setIntPref( + "permissions.default.camera", + SitePermissions.ALLOW + ); + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // Check that the preference is ignored if there is a value. + SitePermissions.setForPrincipal(principal, "camera", SitePermissions.BLOCK); + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // The preference should be honored again, after resetting the permissions. + SitePermissions.removeFromPrincipal(principal, "camera"); + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + // Should be UNKNOWN after clearing the pref. + Services.prefs.clearUserPref("permissions.default.camera"); + Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }); +}); + +add_task(async function testCanvasPermission() { + let resistFingerprinting = Services.prefs.getBoolPref( + "privacy.resistFingerprinting", + false + ); + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" + ); + + SitePermissions.setForPrincipal(principal, "canvas", SitePermissions.ALLOW); + + // Canvas permission is hidden when privacy.resistFingerprinting is false. + Services.prefs.setBoolPref("privacy.resistFingerprinting", false); + Assert.equal(SitePermissions.listPermissions().indexOf("canvas"), -1); + Assert.equal( + SitePermissions.getAllByPrincipal(principal).filter( + permission => permission.id === "canvas" + ).length, + 0 + ); + + // Canvas permission is visible when privacy.resistFingerprinting is true. + Services.prefs.setBoolPref("privacy.resistFingerprinting", true); + Assert.notEqual(SitePermissions.listPermissions().indexOf("canvas"), -1); + Assert.notEqual( + SitePermissions.getAllByPrincipal(principal).filter( + permission => permission.id === "canvas" + ).length, + 0 + ); + + SitePermissions.removeFromPrincipal(principal, "canvas"); + Services.prefs.setBoolPref( + "privacy.resistFingerprinting", + resistFingerprinting + ); +}); + +add_task(async function testFilePermissions() { + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "file:///example.js" + ); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []); + + SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [ + { + id: "camera", + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + ]); + SitePermissions.removeFromPrincipal(principal, "camera"); + Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []); +}); |