Title: [184434] trunk
Revision
184434
Author
an...@apple.com
Date
2015-05-16 06:53:21 -0700 (Sat, 16 May 2015)

Log Message

When redirecting to data URL use HTTP response for same origin policy checks
https://bugs.webkit.org/show_bug.cgi?id=145054
rdar://problem/20299050

Reviewed by Alexey Proskuryakov.

Source/WebCore:

Test: http/tests/security/canvas-remote-read-data-url-image-redirect.html

* dom/ScriptElement.cpp:
(WebCore::ScriptElement::notifyFinished):
* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::sanitizeScriptError):
* html/canvas/CanvasRenderingContext.cpp:
(WebCore::CanvasRenderingContext::wouldTaintOrigin):
* loader/ImageLoader.cpp:
(WebCore::ImageLoader::notifyFinished):
* loader/MediaResourceLoader.cpp:
(WebCore::MediaResourceLoader::responseReceived):
* loader/TextTrackLoader.cpp:
(WebCore::TextTrackLoader::notifyFinished):
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::isOriginClean):
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::passesAccessControlCheck):
(WebCore::CachedResource::passesSameOriginPolicyCheck):

    Factor repeatedly used same origin policy test into a function.

(WebCore::CachedResource::redirectReceived):

    When redirecting to a data URL save the redirect response.

(WebCore::CachedResource::responseForSameOriginPolicyChecks):

    In case we got redirected to data use that response instead of the final data response for policy checks.

* loader/cache/CachedResource.h:

LayoutTests:

* http/tests/security/canvas-remote-read-data-url-image-redirect-expected.txt: Added.
* http/tests/security/canvas-remote-read-data-url-image-redirect.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (184433 => 184434)


--- trunk/LayoutTests/ChangeLog	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/LayoutTests/ChangeLog	2015-05-16 13:53:21 UTC (rev 184434)
@@ -1,3 +1,14 @@
+2015-05-15  Antti Koivisto  <an...@apple.com>
+
+        When redirecting to data URL use HTTP response for same origin policy checks
+        https://bugs.webkit.org/show_bug.cgi?id=145054
+        rdar://problem/20299050
+
+        Reviewed by Alexey Proskuryakov.
+
+        * http/tests/security/canvas-remote-read-data-url-image-redirect-expected.txt: Added.
+        * http/tests/security/canvas-remote-read-data-url-image-redirect.html: Added.
+
 2015-05-15  Simon Fraser  <simon.fra...@apple.com>
 
         REGRESSION (r183300): Background missing on top links on apple.com

Added: trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect-expected.txt (0 => 184434)


--- trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect-expected.txt	2015-05-16 13:53:21 UTC (rev 184434)
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: line 17: Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
+CONSOLE MESSAGE: line 17: Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
+PASS: Calling getImageData() from a canvas tainted by a redirected data URL image was not allowed - Threw error: Error: SecurityError: DOM Exception 18.
+PASS: Calling toDataURL() on a canvas tainted by a redirected data URL image was not allowed - Threw error: Error: SecurityError: DOM Exception 18.
+PASS: Calling getImageData() from a canvas tainted by a redirected data URL image pattern was not allowed - Threw error: Error: SecurityError: DOM Exception 18.
+PASS: Calling toDataURL() on a canvas tainted by a redirected data URL image pattern was not allowed - Threw error: Error: SecurityError: DOM Exception 18.
+

Added: trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect.html (0 => 184434)


--- trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/canvas-remote-read-data-url-image-redirect.html	2015-05-16 13:53:21 UTC (rev 184434)
@@ -0,0 +1,69 @@
+<pre id="console"></pre>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+log = function(msg)
+{
+    document.getElementById('console').appendChild(document.createTextNode(msg + "\n"));
+}
+
+testGetImageData = function(context, description)
+{
+    description = "Calling getImageData() from a canvas tainted by a " + description;
+    try {
+        var imageData = context.getImageData(0,0,100,100);
+        log("FAIL: " + description + " was allowed.");
+    } catch (e) {
+        log("PASS: " + description + " was not allowed - Threw error: " + e + ".");
+    }
+}
+
+testToDataURL = function(canvas, description)
+{
+    description = "Calling toDataURL() on a canvas tainted by a " + description;
+    try {
+        var dataURL = canvas.toDataURL();
+        log("FAIL: " + description + " was allowed.");
+    } catch (e) {
+        log("PASS: " + description + " was not allowed - Threw error: " + e + ".");
+    }
+}
+
+test = function(canvas, description)
+{
+    testGetImageData(canvas.getContext("2d"), description);
+    testToDataURL(canvas, description);
+}
+
+var image = new Image();
+image._onload_ = function() {
+    var canvas = document.createElement("canvas");
+    canvas.width = 100;
+    canvas.height = 100;
+    var context = canvas.getContext("2d");
+
+    // Test reading from a canvas after drawing a data URL image onto it
+    context.drawImage(image, 0, 0, 100, 100);
+
+    test(canvas, "redirected data URL image");
+
+    // Test reading after using a data URL pattern
+    canvas = document.createElement("canvas");
+    canvas.width = 100;
+    canvas.height = 100;
+    var context = canvas.getContext("2d");
+    var remoteImagePattern = context.createPattern(image, "repeat");
+    context.fillStyle = remoteImagePattern;
+    context.fillRect(0, 0, 100, 100);
+
+    test(canvas, "redirected data URL image pattern");
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+image.src = ""
+</script>

Modified: trunk/Source/WebCore/ChangeLog (184433 => 184434)


--- trunk/Source/WebCore/ChangeLog	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/ChangeLog	2015-05-16 13:53:21 UTC (rev 184434)
@@ -1,3 +1,43 @@
+2015-05-15  Antti Koivisto  <an...@apple.com>
+
+        When redirecting to data URL use HTTP response for same origin policy checks
+        https://bugs.webkit.org/show_bug.cgi?id=145054
+        rdar://problem/20299050
+
+        Reviewed by Alexey Proskuryakov.
+
+        Test: http/tests/security/canvas-remote-read-data-url-image-redirect.html
+
+        * dom/ScriptElement.cpp:
+        (WebCore::ScriptElement::notifyFinished):
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::sanitizeScriptError):
+        * html/canvas/CanvasRenderingContext.cpp:
+        (WebCore::CanvasRenderingContext::wouldTaintOrigin):
+        * loader/ImageLoader.cpp:
+        (WebCore::ImageLoader::notifyFinished):
+        * loader/MediaResourceLoader.cpp:
+        (WebCore::MediaResourceLoader::responseReceived):
+        * loader/TextTrackLoader.cpp:
+        (WebCore::TextTrackLoader::notifyFinished):
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::isOriginClean):
+        * loader/cache/CachedResource.cpp:
+        (WebCore::CachedResource::passesAccessControlCheck):
+        (WebCore::CachedResource::passesSameOriginPolicyCheck):
+
+            Factor repeatedly used same origin policy test into a function.
+
+        (WebCore::CachedResource::redirectReceived):
+
+            When redirecting to a data URL save the redirect response.
+
+        (WebCore::CachedResource::responseForSameOriginPolicyChecks):
+
+            In case we got redirected to data use that response instead of the final data response for policy checks.
+
+        * loader/cache/CachedResource.h:
+
 2015-05-16  Jon Lee  <jon...@apple.com>
 
         [iOS] wireless playback picker button is drawn incorrectly

Modified: trunk/Source/WebCore/dom/ScriptElement.cpp (184433 => 184434)


--- trunk/Source/WebCore/dom/ScriptElement.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/dom/ScriptElement.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -336,10 +336,7 @@
     if (!m_cachedScript)
         return;
 
-    if (m_requestUsesAccessControl
-        && !m_element.document().securityOrigin()->canRequest(m_cachedScript->response().url())
-        && !m_cachedScript->passesAccessControlCheck(m_element.document().securityOrigin())) {
-
+    if (m_requestUsesAccessControl && !m_cachedScript->passesSameOriginPolicyCheck(*m_element.document().securityOrigin())) {
         dispatchErrorEvent();
         DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin script load denied by Cross-Origin Resource Sharing policy.")));
         m_element.document().addConsoleMessage(MessageSource::JS, MessageLevel::Error, consoleMessage);

Modified: trunk/Source/WebCore/dom/ScriptExecutionContext.cpp (184433 => 184434)


--- trunk/Source/WebCore/dom/ScriptExecutionContext.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/dom/ScriptExecutionContext.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -347,7 +347,7 @@
 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* cachedScript)
 {
     URL targetURL = completeURL(sourceURL);
-    if (securityOrigin()->canRequest(targetURL) || (cachedScript && cachedScript->passesAccessControlCheck(securityOrigin())))
+    if (securityOrigin()->canRequest(targetURL) || (cachedScript && cachedScript->passesAccessControlCheck(*securityOrigin())))
         return false;
     errorMessage = "Script error.";
     sourceURL = String();

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp (184433 => 184434)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -64,7 +64,7 @@
     if (!cachedImage->image()->hasSingleSecurityOrigin())
         return true;
 
-    return wouldTaintOrigin(cachedImage->response().url()) && !cachedImage->passesAccessControlCheck(canvas()->securityOrigin());
+    return wouldTaintOrigin(cachedImage->responseForSameOriginPolicyChecks().url()) && !cachedImage->passesAccessControlCheck(*canvas()->securityOrigin());
 }
 
 bool CanvasRenderingContext::wouldTaintOrigin(const HTMLVideoElement* video)

Modified: trunk/Source/WebCore/loader/ImageLoader.cpp (184433 => 184434)


--- trunk/Source/WebCore/loader/ImageLoader.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/ImageLoader.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -285,10 +285,7 @@
     if (!m_hasPendingLoadEvent)
         return;
 
-    if (element().fastHasAttribute(HTMLNames::crossoriginAttr)
-        && !element().document().securityOrigin()->canRequest(image()->response().url())
-        && !resource->passesAccessControlCheck(element().document().securityOrigin())) {
-
+    if (element().fastHasAttribute(HTMLNames::crossoriginAttr) && !resource->passesSameOriginPolicyCheck(*element().document().securityOrigin())) {
         clearImageWithoutConsideringPendingLoadEvent();
 
         m_hasPendingErrorEvent = true;

Modified: trunk/Source/WebCore/loader/MediaResourceLoader.cpp (184433 => 184434)


--- trunk/Source/WebCore/loader/MediaResourceLoader.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/MediaResourceLoader.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -94,9 +94,7 @@
     ASSERT_UNUSED(resource, resource == m_resource);
 
     RefPtr<MediaResourceLoader> protect(this);
-    if (!m_crossOriginMode.isNull()
-        && !m_document.securityOrigin()->canRequest(resource->response().url())
-        && !resource->passesAccessControlCheck(m_document.securityOrigin())) {
+    if (!m_crossOriginMode.isNull() && !resource->passesSameOriginPolicyCheck(*m_document.securityOrigin())) {
         static NeverDestroyed<const String> consoleMessage("Cross-origin media resource load denied by Cross-Origin Resource Sharing policy.");
         m_document.addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage.get());
         m_didPassAccessControlCheck = false;

Modified: trunk/Source/WebCore/loader/TextTrackLoader.cpp (184433 => 184434)


--- trunk/Source/WebCore/loader/TextTrackLoader.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/TextTrackLoader.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -125,12 +125,8 @@
     ASSERT(m_resource == resource);
 
     Document* document = downcast<Document>(m_scriptExecutionContext);
-    if (!m_crossOriginMode.isNull()
-        && !document->securityOrigin()->canRequest(resource->response().url())
-        && !resource->passesAccessControlCheck(document->securityOrigin())) {
-
+    if (!m_crossOriginMode.isNull() && !resource->passesSameOriginPolicyCheck(*document->securityOrigin()))
         corsPolicyPreventedLoad();
-    }
 
     if (m_state != Failed) {
         processNewCueData(resource);

Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (184433 => 184434)


--- trunk/Source/WebCore/loader/cache/CachedImage.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -510,9 +510,9 @@
 {
     if (!image()->hasSingleSecurityOrigin())
         return false;
-    if (passesAccessControlCheck(securityOrigin))
+    if (passesAccessControlCheck(*securityOrigin))
         return true;
-    return !securityOrigin->taintsCanvas(response().url());
+    return !securityOrigin->taintsCanvas(responseForSameOriginPolicyChecks().url());
 }
 
 CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const

Modified: trunk/Source/WebCore/loader/cache/CachedResource.cpp (184433 => 184434)


--- trunk/Source/WebCore/loader/cache/CachedResource.cpp	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/cache/CachedResource.cpp	2015-05-16 13:53:21 UTC (rev 184434)
@@ -333,12 +333,19 @@
         m_status = Cached;
 }
 
-bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
+bool CachedResource::passesAccessControlCheck(SecurityOrigin& securityOrigin)
 {
     String errorDescription;
-    return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
+    return WebCore::passesAccessControlCheck(response(), resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, &securityOrigin, errorDescription);
 }
 
+bool CachedResource::passesSameOriginPolicyCheck(SecurityOrigin& securityOrigin)
+{
+    if (securityOrigin.canRequest(responseForSameOriginPolicyChecks().url()))
+        return true;
+    return passesAccessControlCheck(securityOrigin);
+}
+
 bool CachedResource::isExpired() const
 {
     if (m_response.isNull())
@@ -362,14 +369,24 @@
     return computeFreshnessLifetimeForHTTPFamily(response, m_responseTimestamp);
 }
 
-void CachedResource::redirectReceived(ResourceRequest&, const ResourceResponse& response)
+void CachedResource::redirectReceived(ResourceRequest& request, const ResourceResponse& response)
 {
     m_requestedFromNetworkingLayer = true;
     if (response.isNull())
         return;
+
+    // Redirect to data: URL uses the last HTTP response for SOP.
+    if (response.isHTTP() && request.url().protocolIsData())
+        m_redirectResponseForSameOriginPolicyChecks = response;
+
     updateRedirectChainStatus(m_redirectChainCacheStatus, response);
 }
 
+const ResourceResponse& CachedResource::responseForSameOriginPolicyChecks() const
+{
+    return m_redirectResponseForSameOriginPolicyChecks.isNull() ? m_response : m_redirectResponseForSameOriginPolicyChecks;
+}
+
 void CachedResource::responseReceived(const ResourceResponse& response)
 {
     setResponse(response);

Modified: trunk/Source/WebCore/loader/cache/CachedResource.h (184433 => 184434)


--- trunk/Source/WebCore/loader/cache/CachedResource.h	2015-05-16 07:52:22 UTC (rev 184433)
+++ trunk/Source/WebCore/loader/cache/CachedResource.h	2015-05-16 13:53:21 UTC (rev 184434)
@@ -180,7 +180,8 @@
     // Updates the expire date on the cache entry file
     void finish();
 
-    bool passesAccessControlCheck(SecurityOrigin*);
+    bool passesAccessControlCheck(SecurityOrigin&);
+    bool passesSameOriginPolicyCheck(SecurityOrigin&);
 
     // Called by the cache if the object has been removed from the cache
     // while still being referenced. This means the object should delete itself
@@ -197,6 +198,8 @@
     virtual void responseReceived(const ResourceResponse&);
     void setResponse(const ResourceResponse& response) { m_response = response; }
     const ResourceResponse& response() const { return m_response; }
+    // This is the same as response() except after HTTP redirect to data: URL.
+    const ResourceResponse& responseForSameOriginPolicyChecks() const;
 
     bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; }
     bool hasOneHandle() const { return m_handleCount == 1; }
@@ -268,6 +271,7 @@
     RefPtr<SubresourceLoader> m_loader;
     ResourceLoaderOptions m_options;
     ResourceResponse m_response;
+    ResourceResponse m_redirectResponseForSameOriginPolicyChecks;
     RefPtr<SharedBuffer> m_data;
     DeferrableOneShotTimer m_decodedDataDeletionTimer;
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to