mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1644456 - Manage HTTPS-Only Mode permission with SitePermissions interface. r=necko-reviewers,pbz,dragana
Differential Revision: https://phabricator.services.mozilla.com/D79427
This commit is contained in:
parent
ae8dc932e1
commit
c44ea283e8
@ -1485,7 +1485,8 @@ var gIdentityHandler = {
|
||||
|
||||
if (
|
||||
(aPermission.id == "popup" && !isPolicyPermission) ||
|
||||
aPermission.id == "autoplay-media"
|
||||
aPermission.id == "autoplay-media" ||
|
||||
aPermission.id == "https-only-load-insecure"
|
||||
) {
|
||||
let menulist = document.createXULElement("menulist");
|
||||
let menupopup = document.createXULElement("menupopup");
|
||||
|
@ -47,3 +47,4 @@ permission.persistent-storage.label = Store Data in Persistent Storage
|
||||
permission.canvas.label = Extract Canvas Data
|
||||
permission.midi.label = Access MIDI Devices
|
||||
permission.midi-sysex.label = Access MIDI Devices with SysEx Support
|
||||
permission.https-only-load-insecure.label = Use insecure HTTP
|
||||
|
@ -263,6 +263,8 @@ var SitePermissions = {
|
||||
PROMPT: Services.perms.PROMPT_ACTION,
|
||||
ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION,
|
||||
AUTOPLAY_BLOCKED_ALL: Ci.nsIAutoplay.BLOCKED_ALL,
|
||||
ALLOW_INSECURE_LOAD_FOR_SESSION:
|
||||
Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION,
|
||||
|
||||
// Permission scopes.
|
||||
SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
|
||||
@ -302,9 +304,17 @@ var SitePermissions = {
|
||||
if (permission.type == "canvas" && !this.resistFingerprinting) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hide exception permission when HTTPS-Only Mode is disabled.
|
||||
if (
|
||||
permission.type == "https-only-load-insecure" &&
|
||||
!this.httpsOnlyModeEnabled
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Hide persistent storage permission when extension principal
|
||||
* have WebExtensions-unlimitedStorage permission. */
|
||||
|
||||
if (
|
||||
permission.type == "persistent-storage" &&
|
||||
SitePermissions.getForPrincipal(
|
||||
@ -428,6 +438,12 @@ var SitePermissions = {
|
||||
if (!this.resistFingerprinting) {
|
||||
permissions = permissions.filter(permission => permission !== "canvas");
|
||||
}
|
||||
// Hide exception permission when HTTPS-Only Mode is disabled.
|
||||
if (!this.httpsOnlyModeEnabled) {
|
||||
permissions = permissions.filter(
|
||||
permission => permission !== "https-only-load-insecure"
|
||||
);
|
||||
}
|
||||
this._permissionsArray = permissions;
|
||||
}
|
||||
|
||||
@ -435,7 +451,7 @@ var SitePermissions = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the privacy.resistFingerprinting preference changes its value.
|
||||
* Called when a preference changes its value.
|
||||
*
|
||||
* @param {string} data
|
||||
* The last argument passed to the preference change observer
|
||||
@ -444,7 +460,7 @@ var SitePermissions = {
|
||||
* @param {string} latest
|
||||
* The latest value of the preference
|
||||
*/
|
||||
onResistFingerprintingChanged(data, previous, latest) {
|
||||
invalidatePermissionList(data, previous, latest) {
|
||||
// Ensure that listPermissions() will reconstruct its return value the next
|
||||
// time it's called.
|
||||
this._permissionsArray = null;
|
||||
@ -651,6 +667,15 @@ var SitePermissions = {
|
||||
);
|
||||
}
|
||||
|
||||
if (state == this.ALLOW_INSECURE_LOAD_FOR_SESSION) {
|
||||
if (permissionID !== "https-only-load-insecure") {
|
||||
throw new Error(
|
||||
"ALLOW_INSECURE_LOAD_FOR_SESSION can only be set on the https-only-load-insecure permission"
|
||||
);
|
||||
}
|
||||
scope = this.SCOPE_SESSION;
|
||||
}
|
||||
|
||||
// Save temporary permissions.
|
||||
if (scope == this.SCOPE_TEMPORARY) {
|
||||
// We do not support setting temp ALLOW for security reasons.
|
||||
@ -807,6 +832,7 @@ var SitePermissions = {
|
||||
case this.ALLOW:
|
||||
return gStringBundle.GetStringFromName("state.multichoice.allow");
|
||||
case this.ALLOW_COOKIES_FOR_SESSION:
|
||||
case this.ALLOW_INSECURE_LOAD_FOR_SESSION:
|
||||
return gStringBundle.GetStringFromName(
|
||||
"state.multichoice.allowForSession"
|
||||
);
|
||||
@ -846,6 +872,7 @@ var SitePermissions = {
|
||||
}
|
||||
return gStringBundle.GetStringFromName("state.current.allowed");
|
||||
case this.ALLOW_COOKIES_FOR_SESSION:
|
||||
case this.ALLOW_INSECURE_LOAD_FOR_SESSION:
|
||||
return gStringBundle.GetStringFromName(
|
||||
"state.current.allowedForSession"
|
||||
);
|
||||
@ -1037,6 +1064,19 @@ var gPermissionObject = {
|
||||
return SitePermissions.UNKNOWN;
|
||||
},
|
||||
},
|
||||
|
||||
"https-only-load-insecure": {
|
||||
exactHostMatch: true,
|
||||
labelID: "https-only-load-insecure",
|
||||
getDefault() {
|
||||
return SitePermissions.BLOCK;
|
||||
},
|
||||
states: [
|
||||
SitePermissions.BLOCK,
|
||||
SitePermissions.ALLOW_INSECURE_LOAD_FOR_SESSION,
|
||||
SitePermissions.ALLOW,
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) {
|
||||
@ -1058,5 +1098,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
"resistFingerprinting",
|
||||
"privacy.resistFingerprinting",
|
||||
false,
|
||||
SitePermissions.onResistFingerprintingChanged.bind(SitePermissions)
|
||||
SitePermissions.invalidatePermissionList.bind(SitePermissions)
|
||||
);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
SitePermissions,
|
||||
"httpsOnlyModeEnabled",
|
||||
"dom.security.https_only_mode",
|
||||
false,
|
||||
SitePermissions.invalidatePermissionList.bind(SitePermissions)
|
||||
);
|
||||
|
@ -11,6 +11,9 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref(
|
||||
"privacy.resistFingerprinting"
|
||||
);
|
||||
const HTTPS_ONLY_MODE_ENABLED = Services.prefs.getBoolPref(
|
||||
"dom.security.https_only_mode"
|
||||
);
|
||||
const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
|
||||
|
||||
add_task(async function testPermissionsListing() {
|
||||
@ -35,6 +38,10 @@ add_task(async function testPermissionsListing() {
|
||||
// is true.
|
||||
expectedPermissions.push("canvas");
|
||||
}
|
||||
if (HTTPS_ONLY_MODE_ENABLED) {
|
||||
// Exception permission should be hidden unless HTTPS-Only Mode is enabled
|
||||
expectedPermissions.push("https-only-load-insecure");
|
||||
}
|
||||
if (MIDI_ENABLED) {
|
||||
// Should remove this checking and add it as default after it is fully pref'd-on.
|
||||
expectedPermissions.push("midi");
|
||||
@ -193,6 +200,10 @@ add_task(async function testExactHostMatch() {
|
||||
// is true.
|
||||
exactHostMatched.push("canvas");
|
||||
}
|
||||
if (HTTPS_ONLY_MODE_ENABLED) {
|
||||
// Exception permission should be hidden unless HTTPS-Only Mode is enabled
|
||||
exactHostMatched.push("https-only-load-insecure");
|
||||
}
|
||||
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.
|
||||
@ -354,6 +365,52 @@ add_task(async function testCanvasPermission() {
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testHttpsOnlyPermission() {
|
||||
let originalValue = Services.prefs.getBoolPref(
|
||||
"dom.security.https_only_mode",
|
||||
false
|
||||
);
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
"https://example.com"
|
||||
);
|
||||
|
||||
SitePermissions.setForPrincipal(
|
||||
principal,
|
||||
"https-only-load-insecure",
|
||||
SitePermissions.ALLOW
|
||||
);
|
||||
|
||||
// Exception permission is hidden when HTTPS-Only Mode is disabled
|
||||
Services.prefs.setBoolPref("dom.security.https_only_mode", false);
|
||||
Assert.equal(
|
||||
SitePermissions.listPermissions().indexOf("https-only-load-insecure"),
|
||||
-1
|
||||
);
|
||||
Assert.equal(
|
||||
SitePermissions.getAllByPrincipal(principal).filter(
|
||||
permission => permission.id === "https-only-load-insecure"
|
||||
).length,
|
||||
0
|
||||
);
|
||||
|
||||
// Exception permission should show up when HTTPS-Only Mode is enabled
|
||||
Services.prefs.setBoolPref("dom.security.https_only_mode", true);
|
||||
Assert.notEqual(
|
||||
SitePermissions.listPermissions().indexOf("https-only-load-insecure"),
|
||||
-1
|
||||
);
|
||||
Assert.notEqual(
|
||||
SitePermissions.getAllByPrincipal(principal).filter(
|
||||
permission => permission.id === "https-only-load-insecure"
|
||||
).length,
|
||||
0
|
||||
);
|
||||
|
||||
// Reset everything
|
||||
SitePermissions.removeFromPrincipal(principal, "https-only-load-insecure");
|
||||
Services.prefs.setBoolPref("dom.security.https_only_mode", originalValue);
|
||||
});
|
||||
|
||||
add_task(async function testFilePermissions() {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
"file:///example.js"
|
||||
|
@ -77,6 +77,7 @@
|
||||
skin/classic/browser/notification-icons/desktop-notification.svg (../shared/notification-icons/desktop-notification.svg)
|
||||
skin/classic/browser/notification-icons/drag-indicator.svg (../shared/notification-icons/drag-indicator.svg)
|
||||
skin/classic/browser/notification-icons/focus-tab-by-prompt.svg (../shared/notification-icons/focus-tab-by-prompt.svg)
|
||||
skin/classic/browser/notification-icons/https-only-load-insecure-blocked.svg (../shared/notification-icons/https-only-load-insecure-blocked.svg)
|
||||
skin/classic/browser/notification-icons/indexedDB.svg (../shared/notification-icons/indexedDB.svg)
|
||||
skin/classic/browser/notification-icons/microphone-blocked.svg (../shared/notification-icons/microphone-blocked.svg)
|
||||
skin/classic/browser/notification-icons/microphone-detailed.svg (../shared/notification-icons/microphone-detailed.svg)
|
||||
|
@ -174,6 +174,10 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
|
||||
}
|
||||
|
||||
.https-only-load-insecure-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/https-only-load-insecure-blocked.svg);
|
||||
}
|
||||
|
||||
#canvas-notification-icon,
|
||||
.popup-notification-icon[popupid="canvas-permissions-prompt"],
|
||||
.canvas-icon {
|
||||
|
@ -0,0 +1,7 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M14.71,1.32a1,1,0,0,0-1.42,0L11.48,3.1A4,4,0,0,0,4,5V7H3.5A1.5,1.5,0,0,0,2,8.48v4.11l-.71.7A1,1,0,0,0,1,14a1,1,0,0,0,1,1,1,1,0,0,0,.71-.29l12-12A1,1,0,0,0,14.71,1.32ZM6,7V5a2,2,0,0,1,4-.38L7.6,7Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
|
||||
<path d="M12.5,7H12L4,15h8.5A1.5,1.5,0,0,0,14,13.5v-5A1.5,1.5,0,0,0,12.5,7Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
|
||||
</svg>
|
After Width: | Height: | Size: 706 B |
@ -9064,8 +9064,7 @@ nsIPrincipal* nsDocShell::GetInheritedPrincipal(
|
||||
nsCOMPtr<nsIPrincipal> permissionPrincipal =
|
||||
BasePrincipal::CreateContentPrincipal(aLoadState->URI(), attrs);
|
||||
|
||||
if (nsContentUtils::IsExactSitePermAllow(permissionPrincipal,
|
||||
"https-only-mode-exception"_ns)) {
|
||||
if (nsHTTPSOnlyUtils::TestHttpsOnlySitePermission(permissionPrincipal)) {
|
||||
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
||||
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
|
||||
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
||||
|
@ -76,3 +76,9 @@ if CONFIG['FUZZING_INTERFACES']:
|
||||
'fuzztest'
|
||||
]
|
||||
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIHttpsOnlyModePermission.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_security'
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHTTPSOnlyUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIHttpsOnlyModePermission.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "prnetdb.h"
|
||||
|
||||
@ -136,6 +138,26 @@ bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsresult aError) {
|
||||
NS_ERROR_FRAME_CRASHED == aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool nsHTTPSOnlyUtils::TestHttpsOnlySitePermission(nsIPrincipal* aPrincipal) {
|
||||
if (!aPrincipal) {
|
||||
// We always deny the permission if we don't have a principal.
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
mozilla::services::GetPermissionManager();
|
||||
NS_ENSURE_TRUE(permMgr, false);
|
||||
|
||||
uint32_t perm;
|
||||
nsresult rv = permMgr->TestExactPermissionFromPrincipal(
|
||||
aPrincipal, "https-only-load-insecure"_ns, &perm);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW ||
|
||||
perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION;
|
||||
}
|
||||
|
||||
/* ------ Logging ------ */
|
||||
|
||||
/* static */
|
||||
|
@ -64,6 +64,13 @@ class nsHTTPSOnlyUtils {
|
||||
bool aFromPrivateWindow,
|
||||
nsIURI* aURI = nullptr);
|
||||
|
||||
/**
|
||||
* Tests is the HTTPS-Only Mode upgrade exception is set for a given principal
|
||||
* @param aPrincipal Principal to check permission for
|
||||
* @return true if exempt from upgrade
|
||||
*/
|
||||
static bool TestHttpsOnlySitePermission(nsIPrincipal* aPrincipal);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Logs localized message to either content console or browser console
|
||||
|
26
dom/security/nsIHttpsOnlyModePermission.idl
Normal file
26
dom/security/nsIHttpsOnlyModePermission.idl
Normal file
@ -0,0 +1,26 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
/**
|
||||
* An interface to test for cookie permissions
|
||||
*/
|
||||
[scriptable, uuid(73f4f039-d6ff-41a7-9eb3-00db57b0b7f4)]
|
||||
interface nsIHttpsOnlyModePermission : nsISupports
|
||||
{
|
||||
/**
|
||||
* nsIPermissionManager permission values
|
||||
*/
|
||||
const uint32_t LOAD_INSECURE_DEFAULT = 0;
|
||||
const uint32_t LOAD_INSECURE_ALLOW = 1;
|
||||
const uint32_t LOAD_INSECURE_BLOCK = 2;
|
||||
|
||||
/**
|
||||
* additional values which do not match
|
||||
* nsIPermissionManager. Keep space available to allow nsIPermissionManager to
|
||||
* add values without colliding. ACCESS_SESSION is not directly returned by
|
||||
* any methods on this interface.
|
||||
*/
|
||||
const uint32_t LOAD_INSECURE_ALLOW_SESSION = 9;
|
||||
};
|
@ -20,6 +20,7 @@
|
||||
#include "mozilla/dom/ClientChannelHelper.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ContentProcessManager.h"
|
||||
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
|
||||
#include "mozilla/dom/SessionHistoryEntry.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
@ -2093,8 +2094,8 @@ DocumentLoadListener::AsyncOnChannelRedirect(
|
||||
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
||||
mChannel, getter_AddRefs(resultPrincipal));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
isHttpsOnlyExempt = nsContentUtils::IsExactSitePermAllow(
|
||||
resultPrincipal, "https-only-mode-exception"_ns);
|
||||
isHttpsOnlyExempt =
|
||||
nsHTTPSOnlyUtils::TestHttpsOnlySitePermission(resultPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +81,8 @@ class AboutHttpsOnlyErrorParent extends JSWindowActorParent {
|
||||
// Create exception for this website that expires with the session.
|
||||
Services.perms.addFromPrincipal(
|
||||
principal,
|
||||
"https-only-mode-exception",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
"https-only-load-insecure",
|
||||
Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user