diff --git a/dom/credentialmanagement/CredentialsContainer.cpp b/dom/credentialmanagement/CredentialsContainer.cpp index d587735725c9..a782daa0ae1b 100644 --- a/dom/credentialmanagement/CredentialsContainer.cpp +++ b/dom/credentialmanagement/CredentialsContainer.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/CredentialsContainer.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/WebAuthnManager.h" +#include "nsContentUtils.h" namespace mozilla { namespace dom { @@ -19,6 +20,63 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CredentialsContainer) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +already_AddRefed +CreateAndReject(nsPIDOMWindowInner* aParent, ErrorResult& aRv) +{ + MOZ_ASSERT(aParent); + + nsCOMPtr global = do_QueryInterface(aParent); + if (NS_WARN_IF(!global)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr promise = Promise::Create(global, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR); + return promise.forget(); +} + +bool +IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent) +{ + // This method returns true if aParent is either not in a frame / iframe, or + // is in a frame or iframe and all ancestors for aParent are the same origin. + // This is useful for Credential Management because we need to prohibit + // iframes, but not break mochitests (which use iframes to embed the tests). + MOZ_ASSERT(aParent); + + if (aParent->IsTopInnerWindow()) { + // Not in a frame or iframe + return true; + } + + // We're in some kind of frame, so let's get the parent and start checking + // the same origin policy + nsINode* node = nsContentUtils::GetCrossDocParentNode(aParent->GetExtantDoc()); + if (NS_WARN_IF(!node)) { + // This is a sanity check, since there has to be a parent. Fail safe. + return false; + } + + // Check that all ancestors are the same origin, repeating until we find a + // null parent + do { + nsresult rv = nsContentUtils::CheckSameOrigin(aParent->GetExtantDoc(), node); + if (NS_FAILED(rv)) { + // same-origin policy is violated + return false; + } + + node = nsContentUtils::GetCrossDocParentNode(node); + } while (node); + + return true; +} + CredentialsContainer::CredentialsContainer(nsPIDOMWindowInner* aParent) : mParent(aParent) { @@ -45,22 +103,42 @@ CredentialsContainer::WrapObject(JSContext* aCx, JS::Handle aGivenPro } already_AddRefed -CredentialsContainer::Get(const CredentialRequestOptions& aOptions) +CredentialsContainer::Get(const CredentialRequestOptions& aOptions, + ErrorResult& aRv) { + if (!IsSameOriginWithAncestors(mParent)) { + return CreateAndReject(mParent, aRv); + } + + // TODO: Check that we're an active document, too. See bug 1409202. + EnsureWebAuthnManager(); return mManager->GetAssertion(aOptions.mPublicKey, aOptions.mSignal); } already_AddRefed -CredentialsContainer::Create(const CredentialCreationOptions& aOptions) +CredentialsContainer::Create(const CredentialCreationOptions& aOptions, + ErrorResult& aRv) { + if (!IsSameOriginWithAncestors(mParent)) { + return CreateAndReject(mParent, aRv); + } + + // TODO: Check that we're an active document, too. See bug 1409202. + EnsureWebAuthnManager(); return mManager->MakeCredential(aOptions.mPublicKey, aOptions.mSignal); } already_AddRefed -CredentialsContainer::Store(const Credential& aCredential) +CredentialsContainer::Store(const Credential& aCredential, ErrorResult& aRv) { + if (!IsSameOriginWithAncestors(mParent)) { + return CreateAndReject(mParent, aRv); + } + + // TODO: Check that we're an active document, too. See bug 1409202. + EnsureWebAuthnManager(); return mManager->Store(aCredential); } diff --git a/dom/credentialmanagement/CredentialsContainer.h b/dom/credentialmanagement/CredentialsContainer.h index e72665b39d1a..f187c5d4c2d6 100644 --- a/dom/credentialmanagement/CredentialsContainer.h +++ b/dom/credentialmanagement/CredentialsContainer.h @@ -33,13 +33,13 @@ public: WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; already_AddRefed - Get(const CredentialRequestOptions& aOptions); + Get(const CredentialRequestOptions& aOptions, ErrorResult& aRv); already_AddRefed - Create(const CredentialCreationOptions& aOptions); + Create(const CredentialCreationOptions& aOptions, ErrorResult& aRv); already_AddRefed - Store(const Credential& aCredential); + Store(const Credential& aCredential, ErrorResult& aRv); private: ~CredentialsContainer(); diff --git a/dom/credentialmanagement/moz.build b/dom/credentialmanagement/moz.build index 8fc7ab7ae9bc..5e1dc38bbd0a 100644 --- a/dom/credentialmanagement/moz.build +++ b/dom/credentialmanagement/moz.build @@ -20,3 +20,5 @@ UNIFIED_SOURCES += [ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' + +MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] diff --git a/dom/credentialmanagement/tests/.eslintrc.js b/dom/credentialmanagement/tests/.eslintrc.js new file mode 100644 index 000000000000..1f36b13c905e --- /dev/null +++ b/dom/credentialmanagement/tests/.eslintrc.js @@ -0,0 +1,10 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ], + "plugins": [ + "mozilla" + ] +}; \ No newline at end of file diff --git a/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html b/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html new file mode 100644 index 000000000000..a63b1185914d --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/frame_credman_iframes.html @@ -0,0 +1,87 @@ + + + + Embedded Frame for Credential Management: Prohibit use in cross-origin iframes + + + + + + + +
+ + + diff --git a/dom/credentialmanagement/tests/mochitest/mochitest.ini b/dom/credentialmanagement/tests/mochitest/mochitest.ini new file mode 100644 index 000000000000..b3e59bed8f8e --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/mochitest.ini @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = + frame_credman_iframes.html +scheme = https +skip-if = !e10s + +[test_credman_iframes.html] diff --git a/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html b/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html new file mode 100644 index 000000000000..0153b07c6b70 --- /dev/null +++ b/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html @@ -0,0 +1,58 @@ + + + Credential Management: Prohibit use in cross-origin iframes + + + + + + +

Credential Management: Prohibit use in cross-origin iframes

+ + +
+

Same Origin Test

+ + +

Cross-Origin Test

+ +
+ + + + diff --git a/dom/webidl/CredentialManagement.webidl b/dom/webidl/CredentialManagement.webidl index ca36a791607c..bcbced8107b1 100644 --- a/dom/webidl/CredentialManagement.webidl +++ b/dom/webidl/CredentialManagement.webidl @@ -15,8 +15,11 @@ interface Credential { [Exposed=Window, SecureContext, Pref="security.webauth.webauthn"] interface CredentialsContainer { + [Throws] Promise get(optional CredentialRequestOptions options); + [Throws] Promise create(optional CredentialCreationOptions options); + [Throws] Promise store(Credential credential); };