Bug 1632474 - Consider src in unsafe allow all checks for feature policy. r=baku

Differential Revision: https://phabricator.services.mozilla.com/D73147
This commit is contained in:
Johann Hofmann 2020-05-12 19:43:23 +00:00
Родитель 16d345e402
Коммит b59132460a
7 изменённых файлов: 107 добавлений и 39 удалений

Просмотреть файл

@ -185,8 +185,8 @@ add_task(async function testUsePersistentPermissionsFirstParty() {
}); });
}); });
// Test that we should prompt if we are in unsafe permission delegation. The // Test that we do not prompt for maybe unsafe permission delegation if the
// prompt popup should include both first and third party origin. // origin of the page is the original src origin.
add_task(async function testPromptInMaybeUnsafePermissionDelegation() { add_task(async function testPromptInMaybeUnsafePermissionDelegation() {
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function( await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
browser browser
@ -194,8 +194,7 @@ add_task(async function testPromptInMaybeUnsafePermissionDelegation() {
// Persistent allow top level origin // Persistent allow top level origin
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION); PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT); await checkGeolocation(browser, "frameAllowsAll", PromptResult.ALLOW);
await checkNotificationBothOrigins(uri.host, "example.org");
SitePermissions.removeFromPrincipal(null, "geo", browser); SitePermissions.removeFromPrincipal(null, "geo", browser);
PermissionTestUtils.remove(uri, "geo"); PermissionTestUtils.remove(uri, "geo");
@ -205,28 +204,25 @@ add_task(async function testPromptInMaybeUnsafePermissionDelegation() {
// Test that we should prompt if we are in unsafe permission delegation and // Test that we should prompt if we are in unsafe permission delegation and
// change location to origin which is not explicitly trusted. The prompt popup // change location to origin which is not explicitly trusted. The prompt popup
// should include both first and third party origin. // should include both first and third party origin.
add_task(async function testPromptChangeLocatioUnsafePermissionDelegation() { add_task(async function testPromptChangeLocationUnsafePermissionDelegation() {
await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function( await BrowserTestUtils.withNewTab(CROSS_SUBFRAME_PAGE, async function(
browser browser
) { ) {
// Persistent allow top level origin // Persistent allow top level origin
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION); PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
// Request change location. let iframe = await SpecialPowers.spawn(browser, [], () => {
await ContentTask.spawn(browser, { host: uri.host }, async function(args) { return content.document.getElementById("frameAllowsAll").browsingContext;
let frame = content.document.getElementById("frameAllowsAll");
await new Promise(resolve => {
function listener() {
frame.removeEventListener("load", listener, true);
resolve();
}
frame.addEventListener("load", listener, true);
frame.contentWindow.location =
"https://test1.example.com/browser/browser/base/content/test/permissions/permissions.html";
});
}); });
let otherURI =
"https://test1.example.com/browser/browser/base/content/test/permissions/permissions.html";
let loaded = BrowserTestUtils.browserLoaded(browser, true, otherURI);
await SpecialPowers.spawn(iframe, [otherURI], async function(_otherURI) {
content.location = _otherURI;
});
await loaded;
await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT); await checkGeolocation(browser, "frameAllowsAll", PromptResult.PROMPT);
await checkNotificationBothOrigins(uri.host, "test1.example.com"); await checkNotificationBothOrigins(uri.host, "test1.example.com");
@ -242,10 +238,24 @@ add_task(async function testExplicitlyAllowedInChain() {
// Persistent allow top level origin // Persistent allow top level origin
PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION); PermissionTestUtils.add(uri, "geo", Perms.ALLOW_ACTION);
const iframeAncestor = await SpecialPowers.spawn(browser, [], () => { let iframeAncestor = await SpecialPowers.spawn(browser, [], () => {
return content.document.getElementById("frameAncestor").browsingContext; return content.document.getElementById("frameAncestor").browsingContext;
}); });
let iframe = await SpecialPowers.spawn(iframeAncestor, [], () => {
return content.document.getElementById("frameAllowsAll").browsingContext;
});
// Change location to check that we actually look at the ancestor chain
// instead of just considering the "same origin as src" rule.
let otherURI =
"https://test2.example.com/browser/browser/base/content/test/permissions/permissions.html";
let loaded = BrowserTestUtils.browserLoaded(browser, true, otherURI);
await SpecialPowers.spawn(iframe, [otherURI], async function(_otherURI) {
content.location = _otherURI;
});
await loaded;
await checkGeolocation( await checkGeolocation(
iframeAncestor, iframeAncestor,
"frameAllowsAll", "frameAllowsAll",

Просмотреть файл

@ -7,6 +7,6 @@
<body> <body>
<iframe id="frameAncestor" <iframe id="frameAncestor"
src="https://test1.example.com/browser/browser/base/content/test/permissions/temporary_permissions_subframe.html" src="https://test1.example.com/browser/browser/base/content/test/permissions/temporary_permissions_subframe.html"
allow="geolocation 'src' https://example.org"></iframe> allow="geolocation https://test1.example.com https://test2.example.com"></iframe>
</body> </body>
</html> </html>

Просмотреть файл

@ -357,7 +357,7 @@ var gTests = [
"expected " + Object.keys(expected).join(" and ") + " to be shared" "expected " + Object.keys(expected).join(" and ") + " to be shared"
); );
await closeStream(false, "frame1"); await closeStream(false, aIframeId);
} else if (aExpect == PromptResult.DENY) { } else if (aExpect == PromptResult.DENY) {
const observerPromise = expectObserverCalled( const observerPromise = expectObserverCalled(
"recording-window-ended" "recording-window-ended"
@ -409,6 +409,46 @@ var gTests = [
PromptResult.ALLOW PromptResult.ALLOW
); );
// Wildcard attributes still get delegation when their src is unchanged.
await checkPersistentPermission(
Perms.PROMPT_ACTION,
"camera",
"frame4",
PromptResult.PROMPT
);
await checkPersistentPermission(
Perms.DENY_ACTION,
"camera",
"frame4",
PromptResult.DENY
);
await checkPersistentPermission(
Perms.ALLOW_ACTION,
"camera",
"frame4",
PromptResult.ALLOW
);
// Wildcard attributes still get delegation when their src is unchanged.
await checkPersistentPermission(
Perms.PROMPT_ACTION,
"microphone",
"frame4",
PromptResult.PROMPT
);
await checkPersistentPermission(
Perms.DENY_ACTION,
"microphone",
"frame4",
PromptResult.DENY
);
await checkPersistentPermission(
Perms.ALLOW_ACTION,
"microphone",
"frame4",
PromptResult.ALLOW
);
await checkPersistentPermission( await checkPersistentPermission(
Perms.PROMPT_ACTION, Perms.PROMPT_ACTION,
"screen", "screen",
@ -428,6 +468,12 @@ var gTests = [
"frame1", "frame1",
PromptResult.PROMPT PromptResult.PROMPT
); );
await checkPersistentPermission(
Perms.ALLOW_ACTION,
"screen",
"frame4",
PromptResult.PROMPT
);
// Denied by default if allow is not defined // Denied by default if allow is not defined
await checkPersistentPermission( await checkPersistentPermission(
@ -541,20 +587,16 @@ var gTests = [
await checkTempPermission("screen"); await checkTempPermission("screen");
}, },
}, },
{
desc:
"Prompt and display both first party and third party origin in maybe unsafe permission delegation",
run: async function checkPromptNoDelegate() {
await promptNoDelegate("test1.example.com");
await promptNoDelegate("test1.example.com", true, false);
await promptNoDelegate("test1.example.com", false, true);
},
},
{ {
desc: desc:
"Don't reprompt while actively sharing in maybe unsafe permission delegation", "Don't reprompt while actively sharing in maybe unsafe permission delegation",
run: async function checkNoRepromptNoDelegate() { run: async function checkNoRepromptNoDelegate() {
// Change location to ensure that we're treated as potentially unsafe.
await promiseChangeLocationFrame(
"frame4",
"https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"
);
// Check that we get a prompt. // Check that we get a prompt.
let observerPromise = expectObserverCalled("getUserMedia:request"); let observerPromise = expectObserverCalled("getUserMedia:request");
let promise = promisePopupNotificationShown("webRTC-shareDevices"); let promise = promisePopupNotificationShown("webRTC-shareDevices");
@ -566,7 +608,7 @@ var gTests = [
is( is(
PopupNotifications.getNotification("webRTC-shareDevices").options PopupNotifications.getNotification("webRTC-shareDevices").options
.secondName, .secondName,
"test1.example.com", "test2.example.com",
"Use third party's origin as secondName" "Use third party's origin as secondName"
); );
@ -610,13 +652,6 @@ var gTests = [
await closeStream(false, "frame4"); await closeStream(false, "frame4");
}, },
}, },
{
desc:
"Prompt and display both first party and third party origin when sharing screen in unsafe permission delegation",
run: async function checkPromptNoDelegateScreenSharing() {
await promptNoDelegateScreenSharing("test1.example.com");
},
},
{ {
desc: desc:
"Change location, prompt and display both first party and third party origin in maybe unsafe permission delegation", "Change location, prompt and display both first party and third party origin in maybe unsafe permission delegation",
@ -646,6 +681,12 @@ var gTests = [
"Prompt and display both first party and third party origin and temporary deny in frame does not change permission scope", "Prompt and display both first party and third party origin and temporary deny in frame does not change permission scope",
skipObserverVerification: true, skipObserverVerification: true,
run: async function checkPromptBothOriginsTempDenyFrame() { run: async function checkPromptBothOriginsTempDenyFrame() {
// Change location to ensure that we're treated as potentially unsafe.
await promiseChangeLocationFrame(
"frame4",
"https://test2.example.com/browser/browser/base/content/test/webrtc/get_user_media.html"
);
// Persistent allowed first party origin // Persistent allowed first party origin
let browser = gBrowser.selectedBrowser; let browser = gBrowser.selectedBrowser;
let uri = gBrowser.selectedBrowser.documentURI; let uri = gBrowser.selectedBrowser.documentURI;

Просмотреть файл

@ -3485,6 +3485,7 @@ nsresult Document::InitFeaturePolicy(nsIChannel* aChannel) {
if (parentPolicy) { if (parentPolicy) {
// Let's inherit the policy from the parent HTMLIFrameElement if it exists. // Let's inherit the policy from the parent HTMLIFrameElement if it exists.
mFeaturePolicy->InheritPolicy(parentPolicy); mFeaturePolicy->InheritPolicy(parentPolicy);
mFeaturePolicy->SetSrcOrigin(parentPolicy->GetSrcOrigin());
} }
// We don't want to parse the http Feature-Policy header if this pref is off. // We don't want to parse the http Feature-Policy header if this pref is off.

Просмотреть файл

@ -119,6 +119,17 @@ void FeaturePolicy::AppendToDeclaredAllowInAncestorChain(
mDeclaredFeaturesInAncestorChain.AppendElement(aFeature); mDeclaredFeaturesInAncestorChain.AppendElement(aFeature);
} }
bool FeaturePolicy::IsSameOriginAsSrc(nsIPrincipal* aPrincipal) const {
MOZ_ASSERT(aPrincipal);
if (!mSrcOrigin) {
return false;
}
return BasePrincipal::Cast(mSrcOrigin)
->Subsumes(aPrincipal, BasePrincipal::ConsiderDocumentDomain);
}
void FeaturePolicy::SetDeclaredPolicy(Document* aDocument, void FeaturePolicy::SetDeclaredPolicy(Document* aDocument,
const nsAString& aPolicyString, const nsAString& aPolicyString,
nsIPrincipal* aSelfOrigin, nsIPrincipal* aSelfOrigin,

Просмотреть файл

@ -80,6 +80,8 @@ class FeaturePolicy final : public nsISupports, public nsWrapperCache {
mDefaultOrigin = aPrincipal; mDefaultOrigin = aPrincipal;
} }
void SetSrcOrigin(nsIPrincipal* aPrincipal) { mSrcOrigin = aPrincipal; }
nsIPrincipal* DefaultOrigin() const { return mDefaultOrigin; } nsIPrincipal* DefaultOrigin() const { return mDefaultOrigin; }
// Inherits the policy from the 'parent' context if it exists. // Inherits the policy from the 'parent' context if it exists.
@ -115,6 +117,8 @@ class FeaturePolicy final : public nsISupports, public nsWrapperCache {
bool AllowsFeatureExplicitlyInAncestorChain(const nsAString& aFeatureName, bool AllowsFeatureExplicitlyInAncestorChain(const nsAString& aFeatureName,
nsIPrincipal* aOrigin) const; nsIPrincipal* aOrigin) const;
bool IsSameOriginAsSrc(nsIPrincipal* aPrincipal) const;
// WebIDL internal methods. // WebIDL internal methods.
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,

Просмотреть файл

@ -165,6 +165,7 @@ bool FeaturePolicyUtils::IsFeatureUnsafeAllowedAll(
MOZ_ASSERT(policy); MOZ_ASSERT(policy);
return policy->HasFeatureUnsafeAllowsAll(aFeatureName) && return policy->HasFeatureUnsafeAllowsAll(aFeatureName) &&
!policy->IsSameOriginAsSrc(aDocument->NodePrincipal()) &&
!policy->AllowsFeatureExplicitlyInAncestorChain( !policy->AllowsFeatureExplicitlyInAncestorChain(
aFeatureName, policy->DefaultOrigin()) && aFeatureName, policy->DefaultOrigin()) &&
!IsSameOriginAsTop(aDocument); !IsSameOriginAsTop(aDocument);