WebKit Bugzilla
Attachment 358599 Details for
Bug 193076
: Prevent cross-site top-level navigations from third-party iframes
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-193076-20190108090427.patch (text/plain), 37.86 KB, created by
Chris Dumez
on 2019-01-08 09:04:28 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Chris Dumez
Created:
2019-01-08 09:04:28 PST
Size:
37.86 KB
patch
obsolete
>Subversion Revision: 239693 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 7f29aa7d9879e468dbb575bd40d28ca9a88254eb..598756fdfa6bf4c6756e7ebb6ebb72f37af4ffd2 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,39 @@ >+2019-01-07 Chris Dumez <cdumez@apple.com> >+ >+ Prevent cross-site top-level navigations from third-party iframes >+ https://bugs.webkit.org/show_bug.cgi?id=193076 >+ <rdar://problem/36074736> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Prevent cross-site top-level navigations from third-party iframes if the following conditions are met: >+ 1. Its tries to navigate the top-level page cross-site (different eTDL+1) >+ 2. The user has never interacted with the third-party iframe or any of its subframes >+ >+ This experiment's intent is to block suspicious main-frame navigations by third-party content. The feature >+ is behind a runtime experimental feature flag, on by default. >+ >+ Tests: http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html >+ http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html >+ http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html >+ http/tests/security/block-top-level-navigations-by-third-party-iframes.html >+ >+ * dom/Document.cpp: >+ (WebCore::printNavigationErrorMessage): >+ (WebCore::Document::canNavigate): >+ (WebCore::Document::canNavigateInternal): >+ (WebCore::Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking): >+ * dom/Document.h: >+ * dom/UserGestureIndicator.cpp: >+ * page/DOMWindow.cpp: >+ (WebCore::DOMWindow::setLocation): >+ * page/DOMWindow.h: >+ * page/Frame.h: >+ * page/Location.cpp: >+ (WebCore::Location::replace): >+ (WebCore::Location::setLocation): >+ * page/Settings.yaml: >+ > 2019-01-07 David Kilzer <ddkilzer@apple.com> > > PlatformECKey should use a std::unique_ptr >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 4954a6d176a1c5d1a93de53ba3d42bf8e0a48e31..07e255ac2dddc4a235147be1591990a28b679f1c 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,15 @@ >+2019-01-07 Chris Dumez <cdumez@apple.com> >+ >+ Prevent cross-site top-level navigations from third-party iframes >+ https://bugs.webkit.org/show_bug.cgi?id=193076 >+ <rdar://problem/36074736> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add experimental feature flag, on by default. >+ >+ * Shared/WebPreferences.yaml: >+ > 2019-01-07 Joseph Pecoraro <pecoraro@apple.com> > > [Cocoa] Add SPI to check if a WKWebView has an inspector frontend >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index f752db873564a31af2e95a4a7088820baf01bfb1..e60a0b48f5c294d097307a15e0fe8ffea336b641 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -456,12 +456,12 @@ static bool canAccessAncestor(const SecurityOrigin& activeSecurityOrigin, Frame* > return false; > } > >-static void printNavigationErrorMessage(Frame* frame, const URL& activeURL, const char* reason) >+static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, const char* reason) > { >- String message = "Unsafe JavaScript attempt to initiate navigation for frame with URL '" + frame->document()->url().string() + "' from frame with URL '" + activeURL.string() + "'. " + reason + "\n"; >+ String message = "Unsafe JavaScript attempt to initiate navigation for frame with URL '" + frame.document()->url().string() + "' from frame with URL '" + activeURL.string() + "'. " + reason + "\n"; > > // FIXME: should we print to the console of the document performing the navigation instead? >- frame->document()->domWindow()->printErrorMessage(message); >+ frame.document()->domWindow()->printErrorMessage(message); > } > > uint64_t Document::s_globalTreeVersion = 0; >@@ -3337,7 +3337,7 @@ SocketProvider* Document::socketProvider() > return m_socketProvider.get(); > } > >-bool Document::canNavigate(Frame* targetFrame) >+bool Document::canNavigate(Frame* targetFrame, const URL& destinationURL) > { > if (!m_frame) > return false; >@@ -3348,30 +3348,45 @@ bool Document::canNavigate(Frame* targetFrame) > if (!targetFrame) > return true; > >+ if (!canNavigateInternal(*targetFrame)) >+ return false; >+ >+ if (isNavigationBlockedByThirdPartyIFrameRedirectBlocking(*targetFrame, destinationURL)) { >+ printNavigationErrorMessage(*targetFrame, url(), "The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame."_s); >+ return false; >+ } >+ >+ return true; >+} >+ >+bool Document::canNavigateInternal(Frame& targetFrame) >+{ >+ ASSERT(m_frame); >+ > // Cases (i), (ii) and (iii) pass the tests from the specifications but might not pass the "security origin" tests. > > // i. A frame can navigate its top ancestor when its 'allow-top-navigation' flag is set (sometimes known as 'frame-busting'). >- if (!isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top()) >+ if (!isSandboxed(SandboxTopNavigation) && &targetFrame == &m_frame->tree().top()) > return true; > > // ii. A frame can navigate its top ancestor when its 'allow-top-navigation-by-user-activation' flag is set and navigation is triggered by user activation. >- if (!isSandboxed(SandboxTopNavigationByUserActivation) && UserGestureIndicator::processingUserGesture() && targetFrame == &m_frame->tree().top()) >+ if (!isSandboxed(SandboxTopNavigationByUserActivation) && UserGestureIndicator::processingUserGesture() && &targetFrame == &m_frame->tree().top()) > return true; > > // iii. A sandboxed frame can always navigate its descendants. >- if (isSandboxed(SandboxNavigation) && targetFrame->tree().isDescendantOf(m_frame)) >+ if (isSandboxed(SandboxNavigation) && targetFrame.tree().isDescendantOf(m_frame)) > return true; > > // From https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate. > // 1. If A is not the same browsing context as B, and A is not one of the ancestor browsing contexts of B, and B is not a top-level browsing context, and A's active document's active sandboxing > // flag set has its sandboxed navigation browsing context flag set, then abort these steps negatively. >- if (m_frame != targetFrame && isSandboxed(SandboxNavigation) && targetFrame->tree().parent() && !targetFrame->tree().isDescendantOf(m_frame)) { >+ if (m_frame != &targetFrame && isSandboxed(SandboxNavigation) && targetFrame.tree().parent() && !targetFrame.tree().isDescendantOf(m_frame)) { > printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors."_s); > return false; > } > > // 2. Otherwise, if B is a top-level browsing context, and is one of the ancestor browsing contexts of A, then: >- if (m_frame != targetFrame && targetFrame == &m_frame->tree().top()) { >+ if (m_frame != &targetFrame && &targetFrame == &m_frame->tree().top()) { > bool triggeredByUserActivation = UserGestureIndicator::processingUserGesture(); > // 1. If this algorithm is triggered by user activation and A's active document's active sandboxing flag set has its sandboxed top-level navigation with user activation browsing context flag set, then abort these steps negatively. > if (triggeredByUserActivation && isSandboxed(SandboxTopNavigationByUserActivation)) { >@@ -3387,7 +3402,7 @@ bool Document::canNavigate(Frame* targetFrame) > > // 3. Otherwise, if B is a top-level browsing context, and is neither A nor one of the ancestor browsing contexts of A, and A's Document's active sandboxing flag set has its > // sandboxed navigation browsing context flag set, and A is not the one permitted sandboxed navigator of B, then abort these steps negatively. >- if (!targetFrame->tree().parent() && m_frame != targetFrame && targetFrame != &m_frame->tree().top() && isSandboxed(SandboxNavigation) && targetFrame->loader().opener() != m_frame) { >+ if (!targetFrame.tree().parent() && m_frame != &targetFrame && &targetFrame != &m_frame->tree().top() && isSandboxed(SandboxNavigation) && targetFrame.loader().opener() != m_frame) { > printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is sandboxed, and is not allowed to navigate this popup."_s); > return false; > } >@@ -3401,7 +3416,7 @@ bool Document::canNavigate(Frame* targetFrame) > // > // See http://www.adambarth.com/papers/2008/barth-jackson-mitchell.pdf for > // historical information about this security check. >- if (canAccessAncestor(securityOrigin(), targetFrame)) >+ if (canAccessAncestor(securityOrigin(), &targetFrame)) > return true; > > // Top-level frames are easier to navigate than other frames because they >@@ -3415,11 +3430,11 @@ bool Document::canNavigate(Frame* targetFrame) > // some way related to the frame being navigate (e.g., by the "opener" > // and/or "parent" relation). Requiring some sort of relation prevents a > // document from navigating arbitrary, unrelated top-level frames. >- if (!targetFrame->tree().parent()) { >- if (targetFrame == m_frame->loader().opener()) >+ if (!targetFrame.tree().parent()) { >+ if (&targetFrame == m_frame->loader().opener()) > return true; > >- if (canAccessAncestor(securityOrigin(), targetFrame->loader().opener())) >+ if (canAccessAncestor(securityOrigin(), targetFrame.loader().opener())) > return true; > } > >@@ -3427,6 +3442,37 @@ bool Document::canNavigate(Frame* targetFrame) > return false; > } > >+// Prevent cross-site top-level redirects from third-party iframes unless the user has ever interacted with the frame. >+bool Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL) >+{ >+ if (!settings().thirdPartyIframeRedirectBlockingEnabled()) >+ return false; >+ >+ // Only prevent top frame navigations by subframes. >+ if (m_frame == &targetFrame || &targetFrame != &m_frame->tree().top()) >+ return false; >+ >+ // Only prevent navigations by subframes that the user has not interacted with. >+ if (m_frame->hasHadUserInteraction()) >+ return false; >+ >+ // Only prevent navigations by unsandboxed iframes. Such navigations by unsandboxed iframes would have already been blocked unless >+ // "allow-top-navigation" / "allow-top-navigation-by-user-activation" was explicitly specified. >+ if (sandboxFlags() != SandboxNone) >+ return false; >+ >+ // Only prevent navigations by third-party iframes. >+ if (canAccessAncestor(securityOrigin(), &targetFrame)) >+ return false; >+ >+ // Only prevent cross-site navigations. >+ auto* targetDocument = targetFrame.document(); >+ if (targetDocument && (targetDocument->securityOrigin().canAccess(SecurityOrigin::create(destinationURL)) || registrableDomainsAreEqual(targetDocument->url(), destinationURL))) >+ return false; >+ >+ return true; >+} >+ > void Document::didRemoveAllPendingStylesheet() > { > if (auto* parser = scriptableDocumentParser()) >diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h >index 8025ef23e6b4e6c8911aa943a2e3dd17e17325bf..77f2dbd68f53f70fbf85c6a80abf77e815c30a26 100644 >--- a/Source/WebCore/dom/Document.h >+++ b/Source/WebCore/dom/Document.h >@@ -706,7 +706,7 @@ public: > #endif > SocketProvider* socketProvider() final; > >- bool canNavigate(Frame* targetFrame); >+ bool canNavigate(Frame* targetFrame, const URL& destinationURL = URL()); > > bool usesStyleBasedEditability() const; > void setHasElementUsingStyleBasedEditability(); >@@ -1644,6 +1644,9 @@ private: > void checkViewportDependentPictures(); > void checkAppearanceDependentPictures(); > >+ bool canNavigateInternal(Frame& targetFrame); >+ bool isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL); >+ > #if ENABLE(INTERSECTION_OBSERVER) > void notifyIntersectionObserversTimerFired(); > #endif >diff --git a/Source/WebCore/dom/UserGestureIndicator.cpp b/Source/WebCore/dom/UserGestureIndicator.cpp >index d1685b9ae5f2c85349639de387340e99c4925b02..f691d386d6bad1ae989d489cf095dc3e47380267 100644 >--- a/Source/WebCore/dom/UserGestureIndicator.cpp >+++ b/Source/WebCore/dom/UserGestureIndicator.cpp >@@ -27,6 +27,7 @@ > #include "UserGestureIndicator.h" > > #include "Document.h" >+#include "Frame.h" > #include "ResourceLoadObserver.h" > #include <wtf/MainThread.h> > #include <wtf/NeverDestroyed.h> >@@ -59,6 +60,12 @@ UserGestureIndicator::UserGestureIndicator(Optional<ProcessingUserGestureState> > if (processInteractionStyle == ProcessInteractionStyle::Immediate) > ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(document->topDocument()); > document->topDocument().setUserDidInteractWithPage(true); >+ if (auto* frame = document->frame()) { >+ if (!frame->hasHadUserInteraction()) { >+ for (; frame; frame = frame->tree().parent()) >+ frame->setHasHadUserInteraction(); >+ } >+ } > } > } > >diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp >index 1759defc070a43d4b6fa2b5fb7618cad1aa43755..2c766f9585487577fb5d6a9d3cb7872ddfee2901 100644 >--- a/Source/WebCore/page/DOMWindow.cpp >+++ b/Source/WebCore/page/DOMWindow.cpp >@@ -2103,7 +2103,7 @@ void DOMWindow::finishedLoading() > } > } > >-void DOMWindow::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString, SetLocationLocking locking) >+void DOMWindow::setLocation(DOMWindow& activeWindow, const URL& completedURL, SetLocationLocking locking) > { > if (!isCurrentlyDisplayedInFrame()) > return; >@@ -2113,15 +2113,7 @@ void DOMWindow::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, con > return; > > auto* frame = this->frame(); >- if (!activeDocument->canNavigate(frame)) >- return; >- >- Frame* firstFrame = firstWindow.frame(); >- if (!firstFrame) >- return; >- >- URL completedURL = firstFrame->document()->completeURL(urlString); >- if (completedURL.isNull()) >+ if (!activeDocument->canNavigate(frame, completedURL)) > return; > > if (isInsecureScriptAccess(activeWindow, completedURL)) >diff --git a/Source/WebCore/page/DOMWindow.h b/Source/WebCore/page/DOMWindow.h >index effd3f7ecaeedf5081964212a05c422215c3f5c8..8a9c80a0b1e152fe475dcd84fa4a5c0db8016c6d 100644 >--- a/Source/WebCore/page/DOMWindow.h >+++ b/Source/WebCore/page/DOMWindow.h >@@ -145,7 +145,7 @@ public: > Navigator& clientInformation() { return navigator(); } > > Location& location(); >- void setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& location, SetLocationLocking = LockHistoryBasedOnGestureState); >+ void setLocation(DOMWindow& activeWindow, const URL& completedURL, SetLocationLocking = LockHistoryBasedOnGestureState); > > DOMSelection* getSelection(); > >diff --git a/Source/WebCore/page/Frame.h b/Source/WebCore/page/Frame.h >index 9faf2437f5721e96a2582127e2eae2643db9b71e..8ad13d6e1ccc27a638157d811ab1cf48ce2e2069 100644 >--- a/Source/WebCore/page/Frame.h >+++ b/Source/WebCore/page/Frame.h >@@ -172,6 +172,9 @@ public: > > bool documentIsBeingReplaced() const { return m_documentIsBeingReplaced; } > >+ bool hasHadUserInteraction() const { return m_hasHadUserInteraction; } >+ void setHasHadUserInteraction() { m_hasHadUserInteraction = true; } >+ > // ======== All public functions below this point are candidates to move out of Frame into another class. ======== > > WEBCORE_EXPORT void injectUserScripts(UserScriptInjectionTime); >@@ -348,6 +351,7 @@ private: > bool m_documentIsBeingReplaced { false }; > unsigned m_navigationDisableCount { 0 }; > unsigned m_selfOnlyRefCount { 0 }; >+ bool m_hasHadUserInteraction { false }; > > protected: > UniqueRef<EventHandler> m_eventHandler; >diff --git a/Source/WebCore/page/Location.cpp b/Source/WebCore/page/Location.cpp >index 1640cfc123ba3762ab56c07fc8e3111f4732a7fb..9691f59cdbd2037ad8eac83944e019c4622405fe 100644 >--- a/Source/WebCore/page/Location.cpp >+++ b/Source/WebCore/page/Location.cpp >@@ -225,15 +225,25 @@ ExceptionOr<void> Location::assign(DOMWindow& activeWindow, DOMWindow& firstWind > return setLocation(activeWindow, firstWindow, url); > } > >-void Location::replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) >+void Location::replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString) > { > auto* frame = this->frame(); > if (!frame) > return; > ASSERT(frame->document()); > ASSERT(frame->document()->domWindow()); >+ >+ Frame* firstFrame = firstWindow.frame(); >+ if (!firstFrame || !firstFrame->document()) >+ return; >+ >+ URL completedURL = firstFrame->document()->completeURL(urlString); >+ // FIXME: The specification says to throw a SyntaxError if the URL is not valid. >+ if (completedURL.isNull()) >+ return; >+ > // We call DOMWindow::setLocation directly here because replace() always operates on the current frame. >- frame->document()->domWindow()->setLocation(activeWindow, firstWindow, url, LockHistoryAndBackForwardList); >+ frame->document()->domWindow()->setLocation(activeWindow, completedURL, LockHistoryAndBackForwardList); > } > > void Location::reload(DOMWindow& activeWindow) >@@ -264,15 +274,26 @@ void Location::reload(DOMWindow& activeWindow) > frame->navigationScheduler().scheduleRefresh(activeDocument); > } > >-ExceptionOr<void> Location::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) >+ExceptionOr<void> Location::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString) > { > auto* frame = this->frame(); > ASSERT(frame); >- if (!activeWindow.document()->canNavigate(frame)) >+ >+ Frame* firstFrame = firstWindow.frame(); >+ if (!firstFrame || !firstFrame->document()) >+ return { }; >+ >+ URL completedURL = firstFrame->document()->completeURL(urlString); >+ // FIXME: The specification says to throw a SyntaxError if the URL is not valid. >+ if (completedURL.isNull()) >+ return { }; >+ >+ if (!activeWindow.document()->canNavigate(frame, completedURL)) > return Exception { SecurityError }; >+ > ASSERT(frame->document()); > ASSERT(frame->document()->domWindow()); >- frame->document()->domWindow()->setLocation(activeWindow, firstWindow, url); >+ frame->document()->domWindow()->setLocation(activeWindow, completedURL); > return { }; > } > >diff --git a/Source/WebCore/page/Settings.yaml b/Source/WebCore/page/Settings.yaml >index cb29eac3effd4b90acce28e5845902dcaf8d7c5a..2b187e13593b4679c15a3b3c3d048d3aa2ba024c 100644 >--- a/Source/WebCore/page/Settings.yaml >+++ b/Source/WebCore/page/Settings.yaml >@@ -332,6 +332,9 @@ requestAnimationFrameEnabled: > HTTPSUpgradeEnabled: > initial: false > >+thirdPartyIframeRedirectBlockingEnabled: >+ initial: true >+ > cookieEnabled: > initial: true > mediaEnabled: >diff --git a/Source/WebCore/platform/network/ResourceRequestBase.h b/Source/WebCore/platform/network/ResourceRequestBase.h >index eb507271ea15c66b2d1e9c9b2d3c39048d84f832..b470274b115dffda108118d146844b2c7668d23f 100644 >--- a/Source/WebCore/platform/network/ResourceRequestBase.h >+++ b/Source/WebCore/platform/network/ResourceRequestBase.h >@@ -260,7 +260,10 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase&, const ResourceRequest > // FIXME: Find a better place for these functions. > inline String toRegistrableDomain(const URL& a) > { >- return ResourceRequestBase::partitionName(a.host().toString()); >+ auto host = a.host().toString(); >+ auto registrableDomain = ResourceRequestBase::partitionName(host); >+ // Fall back to the host if we cannot determine the registrable domain. >+ return registrableDomain.isEmpty() ? host : registrableDomain; > } > > inline bool registrableDomainsAreEqual(const URL& a, const URL& b) >diff --git a/Source/WebKit/Shared/WebPreferences.yaml b/Source/WebKit/Shared/WebPreferences.yaml >index 20614ae3687924deed66b6d2160af23fbe2fb8c5..03a23594ac2699325db58889ac1eed0de492b215 100644 >--- a/Source/WebKit/Shared/WebPreferences.yaml >+++ b/Source/WebKit/Shared/WebPreferences.yaml >@@ -42,6 +42,13 @@ HTTPSUpgradeEnabled: > humanReadableDescription: "Automatic HTTPS upgrade for known supported sites" > category: experimental > >+ThirdPartyIframeRedirectBlockingEnabled: >+ type: bool >+ defaultValue: true >+ humanReadableName: "Block top-level redirects by third-party iframes" >+ humanReadableDescription: "Block top-level redirects by third-party iframes" >+ category: experimental >+ > JavaEnabled: > type: bool > defaultValue: false >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index ccb3f98bf2785ac2ed22c9c0a7e3cc5c3a5174cc..b98a2f4b291d4ab103f9b64ce0d69e0f5058c5f1 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,28 @@ >+2019-01-07 Chris Dumez <cdumez@apple.com> >+ >+ Prevent cross-site top-level navigations from third-party iframes >+ https://bugs.webkit.org/show_bug.cgi?id=193076 >+ <rdar://problem/36074736> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add layout test coverage. >+ >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt: Added. >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html: Added. >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt: Added. >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html: Added. >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt: Added. >+ * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html: Added. >+ * http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt: Added. >+ * http/tests/security/block-top-level-navigations-by-third-party-iframes.html: Added. >+ * http/tests/security/resources/navigate-top-level-frame-to-failure-page.html: Added. >+ * http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html: Added. >+ * http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html: Added. >+ * http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html: Added. >+ * http/tests/security/resources/should-have-loaded.html: Added. >+ * http/tests/security/resources/should-not-have-loaded.html: Added. >+ > 2019-01-07 Youenn Fablet <youenn@apple.com> > > Resync WPT fetch tests to 834eac4 >diff --git a/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php b/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php >index 58ab852f3434a190a68ebfb6e15750c5ee310273..0b7090aa088f3a108c7d1660f2f07cb56cc289d1 100644 >--- a/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php >+++ b/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php >@@ -13,6 +13,10 @@ if (window.testRunner) > $targetAttribute = 'target="' . $_GET["target"] . '"'; > ?> > <a href="<?php echo $_GET['href']; ?>" <?php echo $targetAttribute; ?>>Click</a> >-<script>document.querySelector("a").click()</script> >+<script> >+internals.withUserGesture(() => { >+ document.querySelector("a").click(); >+}); >+</script> > </body> > </html> >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..63cb84f06c5839cae64d2aeccff94e79aa78e916 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt >@@ -0,0 +1,5 @@ >+PASS Did the load >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html >new file mode 100644 >index 0000000000000000000000000000000000000000..6b4c0993eda800b428a5ef5d9bccbb583f41e4d1 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html >@@ -0,0 +1,20 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="/js-test-resources/js-test.js"></script> >+<script> >+description("Test that top-level navigations by a third-party iframe are allowed if the destination URL is same-origin"); >+jsTestIsAsync = true; >+onload = () => { >+ setTimeout(() => { >+ document.getElementById('testFrame').src = "http://localhost:8000/security/resources/navigate-top-level-frame-to-success-page-same-origin.html"; >+ setTimeout(() => { >+ testFailed("Navigation by subframe should not have been blocked"); >+ finishJSTest(); >+ }, 5000); >+ }, 10); >+} >+</script> >+<iframe id="testFrame"></iframe> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..63cb84f06c5839cae64d2aeccff94e79aa78e916 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt >@@ -0,0 +1,5 @@ >+PASS Did the load >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html >new file mode 100644 >index 0000000000000000000000000000000000000000..b211d1956b8050471df4099029fe8da953b23039 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html >@@ -0,0 +1,20 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="/js-test-resources/js-test.js"></script> >+<script> >+description("Test that top-level navigations by a third-party iframe are allowed with a previous user gesture."); >+jsTestIsAsync = true; >+onload = () => { >+ setTimeout(() => { >+ document.getElementById('testFrame').src = "http://localhost:8000/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html"; >+ setTimeout(() => { >+ testFailed("Navigation by subframe should not have been blocked"); >+ finishJSTest(); >+ }, 5000); >+ }, 10); >+} >+</script> >+<iframe id="testFrame"></iframe> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..63cb84f06c5839cae64d2aeccff94e79aa78e916 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt >@@ -0,0 +1,5 @@ >+PASS Did the load >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html >new file mode 100644 >index 0000000000000000000000000000000000000000..966cf6c6e491340e314b510b93588fe25af0cf66 >--- /dev/null >+++ b/LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html >@@ -0,0 +1,20 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="/js-test-resources/js-test.js"></script> >+<script> >+description("Test that top-level navigations by a third-party iframe are allowed with a user gesture."); >+jsTestIsAsync = true; >+onload = () => { >+ setTimeout(() => { >+ document.getElementById('testFrame').src = "http://localhost:8000/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html"; >+ setTimeout(() => { >+ testFailed("Navigation by subframe should not have been blocked"); >+ finishJSTest(); >+ }, 5000); >+ }, 10); >+} >+</script> >+<iframe id="testFrame"></iframe> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt b/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..3d08f69c6497ca061f7970c1e61f233014cc1f71 >--- /dev/null >+++ b/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt >@@ -0,0 +1,16 @@ >+CONSOLE MESSAGE: line 6: Unsafe JavaScript attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame. >+ >+CONSOLE MESSAGE: line 6: SecurityError: The operation is insecure. >+CONSOLE MESSAGE: line 6: Unsafe JavaScript attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame. >+ >+CONSOLE MESSAGE: line 6: SecurityError: The operation is insecure. >+Test blocking of suspicious top-level navigations by a third-party iframe >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS All navigations by subframes have been blocked >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes.html b/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes.html >new file mode 100644 >index 0000000000000000000000000000000000000000..7946380a0e45fa29791b6d95da13ecaacde191c7 >--- /dev/null >+++ b/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes.html >@@ -0,0 +1,21 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="/js-test-resources/js-test.js"></script> >+<script> >+description("Test blocking of suspicious top-level navigations by a third-party iframe"); >+jsTestIsAsync = true; >+onload = () => { >+ setTimeout(() => { >+ document.getElementById('testFrame').src = "http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html"; >+ setTimeout(() => { >+ testPassed("All navigations by subframes have been blocked"); >+ finishJSTest(); >+ }, 100); >+ }, 10); >+} >+</script> >+<iframe src="http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html"></iframe> >+<iframe id="testFrame"></iframe> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html b/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html >index df20f413dd5ebe49c779ff242cdeaceef4b5b39b..aeff32989e73dbde1af3681dfb595caf7c6ec435 100644 >--- a/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html >+++ b/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html >@@ -11,7 +11,9 @@ > > function performTest() > { >- parent.location = "http://localhost:8000/security/frameNavigation/resources/navigation-changed-iframe.html"; >+ internals.withUserGesture(() => { >+ parent.location = "http://localhost:8000/security/frameNavigation/resources/navigation-changed-iframe.html"; >+ }); > } > </script> > </head> >diff --git a/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page.html b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page.html >new file mode 100644 >index 0000000000000000000000000000000000000000..a2c2bf973c82cc1dccd3e0a70554682d2b6677c6 >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page.html >@@ -0,0 +1,10 @@ >+<html> >+<body> >+Success! The navigation was blocked >+<script> >+window.addEventListener("load", e => { >+ top.location = "http://localhost:8000/security/resources/should-not-have-loaded.html"; >+}); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html >new file mode 100644 >index 0000000000000000000000000000000000000000..8e1027c8e4dfff61b442860801763d6a9f6df294 >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html >@@ -0,0 +1,10 @@ >+<html> >+<body> >+Failure! The navigation should not have been blocked >+<script> >+window.addEventListener("load", e => { >+ top.location = "http://127.0.0.1:8000/security/resources/should-have-loaded.html"; >+}); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html >new file mode 100644 >index 0000000000000000000000000000000000000000..e56b5cd39876badffb2fbeef1825badb8ecc3f3f >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html >@@ -0,0 +1,14 @@ >+<html> >+<body> >+Failure! The navigation should not have been blocked >+<script> >+window.addEventListener("load", e => { >+ internals.withUserGesture(() => { >+ setTimeout(() => { >+ top.location = "http://localhost:8000/security/resources/should-have-loaded.html"; >+ }, 0); >+ }); >+}); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html >new file mode 100644 >index 0000000000000000000000000000000000000000..fd1cfb5d6ed9c33cac6a13b60597c576bc4a4b9a >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html >@@ -0,0 +1,12 @@ >+<html> >+<body> >+Failure! The navigation should not have been blocked >+<script> >+window.addEventListener("load", e => { >+ internals.withUserGesture(() => { >+ top.location = "http://localhost:8000/security/resources/should-have-loaded.html"; >+ }); >+}); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/should-have-loaded.html b/LayoutTests/http/tests/security/resources/should-have-loaded.html >new file mode 100644 >index 0000000000000000000000000000000000000000..0d4494bb615f32d7173d4d345b9201c59b73d695 >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/should-have-loaded.html >@@ -0,0 +1,11 @@ >+<!DOCTYPE html> >+<html> >+<script src="/js-test-resources/js-test.js"></script> >+<body> >+<script> >+jsTestIsAsync = true; >+testPassed("Did the load"); >+finishJSTest(); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/should-not-have-loaded.html b/LayoutTests/http/tests/security/resources/should-not-have-loaded.html >new file mode 100644 >index 0000000000000000000000000000000000000000..d7cc247f81cfbec21ee3e292daaa9417dc9bd7b3 >--- /dev/null >+++ b/LayoutTests/http/tests/security/resources/should-not-have-loaded.html >@@ -0,0 +1,11 @@ >+<!DOCTYPE html> >+<html> >+<script src="/js-test-resources/js-test.js"></script> >+<body> >+<script> >+jsTestIsAsync = true; >+testFailed("Should not have loaded"); >+finishJSTest(); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html b/LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html >index d85453b3c08e7c8fdab7d4362d5aecc7dafff899..bbcccbdf75753d4364304326e28dfd87f7d2b7a5 100644 >--- a/LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html >+++ b/LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html >@@ -1,4 +1,6 @@ > <script> >-open("javascript:alert('failed')", "_top"); >-parent.postMessage("", "*"); >+internals.withUserGesture(() => { >+ open("javascript:alert('failed')", "_top"); >+ parent.postMessage("", "*"); >+}); > </script> >diff --git a/LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt b/LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt >index bc1f167ee287774d42ef5e43167605652b46e577..b3052d6f41e698f56cfdad97d1cc92bc639eed1d 100644 >--- a/LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt >+++ b/LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt >@@ -1,3 +1,3 @@ >-CONSOLE MESSAGE: line 2: Blocked a frame with origin "http://localhost:8080" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match. >+CONSOLE MESSAGE: line 3: Blocked a frame with origin "http://localhost:8080" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match. > This test passes if there is no alert dialog. >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 193076
:
358349
|
358516
|
358523
|
358531
|
358559
|
358579
| 358599