<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[199221] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/199221">199221</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-04-08 00:17:50 -0700 (Fri, 08 Apr 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>CSP: Block XHR when calling XMLHttpRequest.send() and throw network error.
https://bugs.webkit.org/show_bug.cgi?id=153598
&lt;rdar://problem/24391483&gt;

Patch by John Wilander &lt;wilander@apple.com&gt; on 2016-04-08
Reviewed by Darin Adler.

Source/WebCore:

No new tests. Changes to existing tests are sufficient.

* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::open):
(WebCore::XMLHttpRequest::initSend):
    Moved the CSP check from XMLHttpRequest::open() to XMLHttpRequest::initSend().
    Changed the thrown error type from Security to Network for synchronous requests.
    Changed from throwing an error to firing an error event for asynchronous requests.
    These changes are in conformance with connect-src of Content Security Policy Level 2.
    https://www.w3.org/TR/CSP2/#directive-connect-src (W3C Candidate Recommendation, 21 July 2015)

LayoutTests:

* fast/workers/resources/worker-inherits-csp-blocks-xhr.js:
(catch):
* fast/workers/worker-inherits-csp-blocks-xhr-expected.txt:
    Changed expected error from DOMException.SECURITY_ERR to DOMException.NETWORK_ERR.
* http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt:
* http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html:
    Now tests that XMLHttpRequest.send() is blocked if the URL voilates the connect-src directive in CSP.
* http/tests/security/contentSecurityPolicy/resources/worker.php:
    Added two additional calls to XMLHttpRequest.send() and switched to receiving an error event to make
    existing tests work with code changes.
* http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html:
    Added an additional call to XMLHttpRequest.send() and switched to receiving an error event to make
    existing test work with code changes.
* http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt:
* http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html:
    Added an additional call to XMLHttpRequest.send() and switched to receiving an error event to make
    existing tests work with code changes.
    Refactored test mechnism with additional parameters to cover synchronous/asynchronous as well as
    same-origin/cross-origin in isolated worlds.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastworkersresourcesworkerinheritscspblocksxhrjs">trunk/LayoutTests/fast/workers/resources/worker-inherits-csp-blocks-xhr.js</a></li>
<li><a href="#trunkLayoutTestsfastworkersworkerinheritscspblocksxhrexpectedtxt">trunk/LayoutTests/fast/workers/worker-inherits-csp-blocks-xhr-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestssecuritycontentSecurityPolicyconnectsrcxmlhttprequestblockedexpectedtxt">trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestssecuritycontentSecurityPolicyconnectsrcxmlhttprequestblockedhtml">trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html</a></li>
<li><a href="#trunkLayoutTestshttptestssecuritycontentSecurityPolicyresourcesworkerphp">trunk/LayoutTests/http/tests/security/contentSecurityPolicy/resources/worker.php</a></li>
<li><a href="#trunkLayoutTestshttptestssecuritycontentSecurityPolicysourcelistparsingmalformedmetahtml">trunk/LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html</a></li>
<li><a href="#trunkLayoutTestshttptestssecurityisolatedWorldbypassmainworldcspforxhrexpectedtxt">trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestssecurityisolatedWorldbypassmainworldcspforxhrhtml">trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorexmlXMLHttpRequestcpp">trunk/Source/WebCore/xml/XMLHttpRequest.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/ChangeLog        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,3 +1,31 @@
</span><ins>+2016-04-08  John Wilander  &lt;wilander@apple.com&gt;
+
+        CSP: Block XHR when calling XMLHttpRequest.send() and throw network error.
+        https://bugs.webkit.org/show_bug.cgi?id=153598
+        &lt;rdar://problem/24391483&gt;
+
+        Reviewed by Darin Adler.
+
+        * fast/workers/resources/worker-inherits-csp-blocks-xhr.js:
+        (catch):
+        * fast/workers/worker-inherits-csp-blocks-xhr-expected.txt:
+            Changed expected error from DOMException.SECURITY_ERR to DOMException.NETWORK_ERR.
+        * http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt:
+        * http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html:
+            Now tests that XMLHttpRequest.send() is blocked if the URL voilates the connect-src directive in CSP.
+        * http/tests/security/contentSecurityPolicy/resources/worker.php:
+            Added two additional calls to XMLHttpRequest.send() and switched to receiving an error event to make
+            existing tests work with code changes.
+        * http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html:
+            Added an additional call to XMLHttpRequest.send() and switched to receiving an error event to make 
+            existing test work with code changes.
+        * http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt:
+        * http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html:
+            Added an additional call to XMLHttpRequest.send() and switched to receiving an error event to make 
+            existing tests work with code changes.
+            Refactored test mechnism with additional parameters to cover synchronous/asynchronous as well as 
+            same-origin/cross-origin in isolated worlds.
+
</ins><span class="cx"> 2016-04-07  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         FontFaceSet binding does not handle null correctly
</span></span></pre></div>
<a id="trunkLayoutTestsfastworkersresourcesworkerinheritscspblocksxhrjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/workers/resources/worker-inherits-csp-blocks-xhr.js (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/workers/resources/worker-inherits-csp-blocks-xhr.js        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/fast/workers/resources/worker-inherits-csp-blocks-xhr.js        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -7,8 +7,8 @@
</span><span class="cx"> } catch (e) {
</span><span class="cx">     exception = e;
</span><span class="cx"> }
</span><del>-// FIXME: We should be throwing a DOMException.NETWORK_ERR. See &lt;https://bugs.webkit.org/show_bug.cgi?id=153598&gt;.
-var expectedExceptionCode = 18; // DOMException.SECURITY_ERR
</del><ins>+
+var expectedExceptionCode = 19; // DOMException.NETWORK_ERR
</ins><span class="cx"> if (!exception)
</span><span class="cx">     self.postMessage(&quot;FAIL should throw &quot; + expectedExceptionCode + &quot;. But did not throw an exception.&quot;);
</span><span class="cx"> else {
</span></span></pre></div>
<a id="trunkLayoutTestsfastworkersworkerinheritscspblocksxhrexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/workers/worker-inherits-csp-blocks-xhr-expected.txt (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/workers/worker-inherits-csp-blocks-xhr-expected.txt        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/fast/workers/worker-inherits-csp-blocks-xhr-expected.txt        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><span class="cx"> CONSOLE MESSAGE: Refused to connect to non-existent-file because it does not appear in the connect-src directive of the Content Security Policy.
</span><span class="cx"> This tests that the Content Security Policy (CSP) of the owner document (this page) blocks a file-URL Web Worker from making an XHR request because the parent's CSP contains &quot;connect-src 'none'&quot;
</span><span class="cx"> 
</span><del>-PASS threw exception Error: SecurityError: DOM Exception 18.
</del><ins>+PASS threw exception Error: NetworkError: DOM Exception 19.
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritycontentSecurityPolicyconnectsrcxmlhttprequestblockedexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><span class="cx"> CONSOLE MESSAGE: Refused to connect to http://localhost:8000/xmlhttprequest/resources/get.txt because it does not appear in the connect-src directive of the Content Security Policy.
</span><del>-Pass
</del><ins>+CONSOLE MESSAGE: Refused to connect to http://localhost:8000/xmlhttprequest/resources/get.txt because it does not appear in the connect-src directive of the Content Security Policy.
+CONSOLE MESSAGE: Refused to connect to http://localhost:8000/xmlhttprequest/resources/get.txt because it does not appear in the connect-src directive of the Content Security Policy.
+This tests that a Content Security Policy violation for an XHR is triggered when calling XMLHttpRequest.send().
</ins><span class="cx"> 
</span><ins>+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS xhrSync.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, false) did not throw exception.
+PASS xhrSync.send() threw exception Error: NetworkError: DOM Exception 19.
+PASS xhrAsync.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, true) did not throw exception.
+PASS xhrAsync.send() did not throw exception.
+PASS xhrAsyncAbort.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, true) did not throw exception.
+PASS xhrAsyncAbort.send();xhrAsyncAbort.abort();  did not throw exception.
+PASS An error event was received for the asynchronous call.
+PASS An error event was received for the aborted asynchronous call.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritycontentSecurityPolicyconnectsrcxmlhttprequestblockedhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -2,27 +2,40 @@
</span><span class="cx"> &lt;html&gt;
</span><span class="cx"> &lt;head&gt;
</span><span class="cx"> &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;connect-src http://127.0.0.1:8000&quot;&gt;
</span><del>-&lt;script&gt;
-if (window.testRunner)
-    testRunner.dumpAsText();
-&lt;/script&gt;
</del><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;/head&gt;
</span><span class="cx"> &lt;body&gt;
</span><del>-&lt;pre id=&quot;console&quot;&gt;&lt;/pre&gt;
</del><span class="cx"> &lt;script&gt;
</span><del>-function log(msg)
-{
-    document.getElementById(&quot;console&quot;).appendChild(document.createTextNode(msg + &quot;\n&quot;));
-}
</del><ins>+description(&quot;This tests that a Content Security Policy violation for an XHR is triggered when calling XMLHttpRequest.send().&quot;);
</ins><span class="cx"> 
</span><del>-try {
-    var xhr = new XMLHttpRequest;
-    xhr.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, true); 
-    log(&quot;Fail&quot;);
-} catch(e) {
-    log(&quot;Pass&quot;);
-}
</del><ins>+jsTestIsAsync = true;
</ins><span class="cx"> 
</span><ins>+var xhrSync = new XMLHttpRequest;
+xhrSync.addEventListener(&quot;error&quot;, function () {
+    debug(&quot;FAIL An error event should not have been received.&quot;);
+});
+
+var xhrAsync = new XMLHttpRequest;
+xhrAsync.addEventListener(&quot;error&quot;, function () {
+    debug(&quot;PASS An error event was received for the asynchronous call.&quot;);
+});
+
+var xhrAsyncAbort = new XMLHttpRequest;
+xhrAsyncAbort.addEventListener(&quot;error&quot;, function () {
+    debug(&quot;PASS An error event was received for the aborted asynchronous call.&quot;);
+    finishJSTest();
+});
+
+shouldNotThrow('xhrSync.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, false)'); // Synchronous request
+shouldThrow(&quot;xhrSync.send()&quot;, &quot;'Error: NetworkError: DOM Exception 19'&quot;);
+
+shouldNotThrow('xhrAsync.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, true)'); // Asynchronous request
+shouldNotThrow(&quot;xhrAsync.send()&quot;);
+
+shouldNotThrow('xhrAsyncAbort.open(&quot;GET&quot;, &quot;http://localhost:8000/xmlhttprequest/resources/get.txt&quot;, true)'); // Asynchronous request
+shouldNotThrow(&quot;xhrAsyncAbort.send();xhrAsyncAbort.abort(); &quot;);
+
</ins><span class="cx"> &lt;/script&gt;
</span><ins>+&lt;script src=&quot;/js-test-resources/js-test-post.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;/body&gt;
</span><span class="cx"> &lt;/html&gt;
</span></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritycontentSecurityPolicyresourcesworkerphp"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/contentSecurityPolicy/resources/worker.php (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/contentSecurityPolicy/resources/worker.php        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/contentSecurityPolicy/resources/worker.php        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -58,13 +58,15 @@
</span><span class="cx"> } else if ($_GET[&quot;type&quot;] == &quot;make-xhr&quot;) {
</span><span class="cx"> ?&gt;
</span><span class="cx"> 
</span><del>-try {
-    var xhr = new XMLHttpRequest;
-    xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, true);
</del><ins>+var xhr = new XMLHttpRequest;
+xhr.addEventListener(&quot;load&quot;, function () {
</ins><span class="cx">     postMessage(&quot;xhr allowed&quot;);
</span><del>-} catch(e) {
</del><ins>+});
+xhr.addEventListener(&quot;error&quot;, function () {
</ins><span class="cx">     postMessage(&quot;xhr blocked&quot;);
</span><del>-}
</del><ins>+});
+xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, true);
+xhr.send();
</ins><span class="cx"> 
</span><span class="cx"> &lt;?php
</span><span class="cx"> } else if ($_GET[&quot;type&quot;] == &quot;set-timeout&quot;) {
</span><span class="lines">@@ -115,13 +117,15 @@
</span><span class="cx"> } else if ($_GET[&quot;type&quot;] == &quot;multiple-headers&quot;) {
</span><span class="cx"> ?&gt;
</span><span class="cx"> 
</span><del>-try {
-    var xhr = new XMLHttpRequest;
-    xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, true);
</del><ins>+var xhr = new XMLHttpRequest;
+xhr.addEventListener(&quot;load&quot;, function () {
</ins><span class="cx">     postMessage(&quot;xhr allowed&quot;);
</span><del>-} catch(e) {
</del><ins>+});
+xhr.addEventListener(&quot;error&quot;, function () {
</ins><span class="cx">     postMessage(&quot;xhr blocked&quot;);
</span><del>-}
</del><ins>+});
+xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, true);
+xhr.send();
</ins><span class="cx"> 
</span><span class="cx"> var id = 0;
</span><span class="cx"> try {
</span></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritycontentSecurityPolicysourcelistparsingmalformedmetahtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -17,7 +17,8 @@
</span><span class="cx"> 
</span><span class="cx"> try {
</span><span class="cx">     var xhr = new XMLHttpRequest;
</span><del>-    xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, true); 
</del><ins>+    xhr.open(&quot;GET&quot;, &quot;http://127.0.0.1:8000/xmlhttprequest/resources/get.txt&quot;, false);
+    xhr.send();
</ins><span class="cx">     log(&quot;Fail&quot;);
</span><span class="cx"> } catch(e) {
</span><span class="cx">     log(&quot;Pass&quot;);
</span></span></pre></div>
<a id="trunkLayoutTestshttptestssecurityisolatedWorldbypassmainworldcspforxhrexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,13 +1,25 @@
</span><del>-CONSOLE MESSAGE: Refused to connect to http://localhost:8000/security/isolatedWorld/resources/cross-origin-xhr.txt because it does not appear in the connect-src directive of the Content Security Policy.
</del><ins>+CONSOLE MESSAGE: Refused to connect to http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi because it does not appear in the connect-src directive of the Content Security Policy.
+CONSOLE MESSAGE: Refused to connect to http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi because it does not appear in the connect-src directive of the Content Security Policy.
</ins><span class="cx"> Tests that isolated worlds can have XHRs that the page's CSP wouldn't allow.
</span><span class="cx"> 
</span><span class="cx"> On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-XHR from main world
-PASS: XHR.open threw an exception.
-XHR from isolated world
-PASS: XHR.open did not throw an exception.
</del><ins>+Synchronous XHR same-origin from main world
+PASS: XHR.send threw an exception.
+Asynchronous XHR same-origin from main world
+PASS: XHR.send did not throw an exception.
+PASS: XHR.send received an error event.
+Synchronous XHR same-origin from isolated world
+PASS: XHR.send did not throw an exception.
+Asynchronous XHR same-origin from isolated world
+PASS: XHR.send did not throw an exception.
+PASS: XHR.send received a load event.
+Synchronous XHR cross-origin from isolated world
+PASS: XHR.send did not throw an exception.
+Asynchronous XHR cross-origin from isolated world
+PASS: XHR.send did not throw an exception.
+PASS: XHR.send received a load event.
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestshttptestssecurityisolatedWorldbypassmainworldcspforxhrhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,7 +1,9 @@
</span><span class="cx"> &lt;!DOCTYPE html&gt;
</span><span class="cx"> &lt;html&gt;
</span><ins>+&lt;head&gt;
</ins><span class="cx">     &lt;script src=&quot;../../js-test-resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;connect-src 'none'&quot;&gt;
</span><ins>+&lt;/head&gt;
</ins><span class="cx"> &lt;body&gt;
</span><span class="cx"> &lt;p id=&quot;description&quot;&gt;&lt;/p&gt;
</span><span class="cx"> &lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
</span><span class="lines">@@ -11,15 +13,38 @@
</span><span class="cx"> 
</span><span class="cx"> jsTestIsAsync = true;
</span><span class="cx"> 
</span><ins>+const SameOrigin = true;
+const CrossOrigin = false;
+const Asynchronous = true;
+const Synchronous = false;
+const ShouldBlock = true;
+const ShouldNotBlock = false;
+
</ins><span class="cx"> var tests = [
</span><span class="cx">     function() {
</span><del>-        debug('XHR from main world');
-        xhr(true);
</del><ins>+        debug('Synchronous XHR same-origin from main world');
+        xhr({sameOrigin : true}, Synchronous, ShouldBlock);
</ins><span class="cx">     },
</span><span class="cx">     function() {
</span><del>-        debug('XHR from isolated world');
-        runTestInWorld(1, 'xhr', 'false');
</del><ins>+        debug('Asynchronous XHR same-origin from main world');
+        xhr({sameOrigin : true}, Asynchronous, ShouldBlock);
</ins><span class="cx">     },
</span><ins>+    function() {
+        debug('Synchronous XHR same-origin from isolated world');
+        invokeInWorld(1, xhr, SameOrigin, Synchronous, ShouldNotBlock);
+    },
+    function() {
+        debug('Asynchronous XHR same-origin from isolated world');
+        invokeInWorld(2, xhr, SameOrigin, Asynchronous, ShouldNotBlock);
+    },
+    function() {
+        debug('Synchronous XHR cross-origin from isolated world');
+        invokeInWorld(3, xhr, CrossOrigin, Synchronous, ShouldNotBlock);
+    },
+    function() {
+        debug('Asynchronous XHR cross-origin from isolated world');
+        invokeInWorld(4, xhr, CrossOrigin, Asynchronous, ShouldNotBlock);
+    }
</ins><span class="cx"> ];
</span><span class="cx"> var currentTest = 0;
</span><span class="cx"> 
</span><span class="lines">@@ -52,14 +77,15 @@
</span><span class="cx">     testFailed('Test depends on LayoutTestController and must be run by DRT');
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-function runTestInWorld(worldId, funcName, param)
-{
-    testRunner.evaluateScriptInIsolatedWorld(
-        worldId, String(eval(funcName)) + &quot;\n&quot; + funcName + &quot;(&quot; + param + &quot;);&quot;);
</del><ins>+// This function will only successfully pass on JSON-stringifieable arguments such as numbers and strings to aNamedFunction
+function invokeInWorld(worldId, aNamedFunction) {
+    console.assert(aNamedFunction.name);
+    var argumentsToPass = Array.prototype.slice.call(arguments, 2);
+    var script = aNamedFunction.toString() + '; ' + aNamedFunction.name + '(' + argumentsToPass.map(JSON.stringify).join(', ') + ');';
+    testRunner.evaluateScriptInIsolatedWorld(worldId, script);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-function xhr(shouldBlock)
-{
</del><ins>+function xhr(isSameOrigin, isAsync, shouldBlock) {
</ins><span class="cx">     function debug(message) {
</span><span class="cx">         window.postMessage(JSON.stringify({
</span><span class="cx">                 'type': 'debug',
</span><span class="lines">@@ -68,25 +94,58 @@
</span><span class="cx">             '*');
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    var url = (isSameOrigin ? 'http://127.0.0.1:8000/' : 'http://localhost:8000/') + 'xmlhttprequest/resources/access-control-basic-allow.cgi';
</ins><span class="cx">     var xhr = new XMLHttpRequest();
</span><ins>+    var asyncCallDone = false;
+    var finallyClauseDone = false;
+
+    xhr.open('GET', url, isAsync);
+
+    if (isAsync) {
+        xhr.addEventListener('load', function() {
+            if (shouldBlock)
+                debug('FAIL: XHR.send should not have received a load event.');
+            else
+                debug('PASS: XHR.send received a load event.');
+
+            if (finallyClauseDone)
+                window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
+            else
+                asyncCallDone = true;
+        });
+
+        xhr.addEventListener('error', function() {
+            if (shouldBlock)
+                debug('PASS: XHR.send received an error event.');
+            else
+                debug('FAIL: XHR.send should not have received an error event.');
+
+            if (finallyClauseDone)
+                window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
+            else
+                asyncCallDone = true;
+        });
+    }
+
</ins><span class="cx">     try {
</span><del>-        xhr.open('GET', 'http://localhost:8000/security/isolatedWorld/resources/cross-origin-xhr.txt', true);
-        if (shouldBlock)
-            debug('FAIL: XHR.open should have thrown an exception.');
</del><ins>+        xhr.send();
+        if (shouldBlock &amp;&amp; !isAsync)
+            debug('FAIL: XHR.send should have thrown an exception.');
</ins><span class="cx">         else
</span><del>-            debug('PASS: XHR.open did not throw an exception.');
</del><ins>+            debug('PASS: XHR.send did not throw an exception.');
</ins><span class="cx">     } catch (e) {
</span><del>-        if (shouldBlock)
-            debug('PASS: XHR.open threw an exception.');
</del><ins>+        if (shouldBlock &amp;&amp; !isAsync)
+            debug('PASS: XHR.send threw an exception.');
</ins><span class="cx">         else
</span><del>-            debug('FAIL: XHR.open should not have thrown an exception.');
</del><ins>+            debug('FAIL: XHR.send should not have thrown an exception.');
</ins><span class="cx">     } finally {
</span><del>-        window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
</del><ins>+        if (!isAsync || asyncCallDone)
+            window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
+        else
+            finallyClauseDone = true;
</ins><span class="cx">     }
</span><span class="cx"> }
</span><del>-
</del><span class="cx"> &lt;/script&gt;
</span><del>-
</del><span class="cx"> &lt;script src=&quot;../../js-test-resources/js-test-post.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;/body&gt;
</span><span class="cx"> &lt;/html&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/Source/WebCore/ChangeLog        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -1,3 +1,22 @@
</span><ins>+2016-04-08  John Wilander  &lt;wilander@apple.com&gt;
+
+        CSP: Block XHR when calling XMLHttpRequest.send() and throw network error.
+        https://bugs.webkit.org/show_bug.cgi?id=153598
+        &lt;rdar://problem/24391483&gt;
+
+        Reviewed by Darin Adler.
+
+        No new tests. Changes to existing tests are sufficient.
+
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::open):
+        (WebCore::XMLHttpRequest::initSend):
+            Moved the CSP check from XMLHttpRequest::open() to XMLHttpRequest::initSend().
+            Changed the thrown error type from Security to Network for synchronous requests.
+            Changed from throwing an error to firing an error event for asynchronous requests.
+            These changes are in conformance with connect-src of Content Security Policy Level 2.
+            https://www.w3.org/TR/CSP2/#directive-connect-src (W3C Candidate Recommendation, 21 July 2015)
+
</ins><span class="cx"> 2016-04-07  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         FontFaceSet binding does not handle null correctly
</span></span></pre></div>
<a id="trunkSourceWebCorexmlXMLHttpRequestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/xml/XMLHttpRequest.cpp (199220 => 199221)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/xml/XMLHttpRequest.cpp        2016-04-08 07:15:43 UTC (rev 199220)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.cpp        2016-04-08 07:17:50 UTC (rev 199221)
</span><span class="lines">@@ -497,13 +497,6 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
-    if (!scriptExecutionContext()-&gt;contentSecurityPolicy()-&gt;allowConnectToSource(url, scriptExecutionContext()-&gt;shouldBypassMainWorldContentSecurityPolicy())) {
-        // FIXME: Should this be throwing an exception?
-        ec = SECURITY_ERR;
-        return;
-    }
-
</del><span class="cx">     if (!async &amp;&amp; scriptExecutionContext()-&gt;isDocument()) {
</span><span class="cx">         if (document()-&gt;settings() &amp;&amp; !document()-&gt;settings()-&gt;syncXHRInDocumentsEnabled()) {
</span><span class="cx">             logConsoleError(scriptExecutionContext(), &quot;Synchronous XMLHttpRequests are disabled for this page.&quot;);
</span><span class="lines">@@ -573,6 +566,17 @@
</span><span class="cx">     }
</span><span class="cx">     ASSERT(!m_loader);
</span><span class="cx"> 
</span><ins>+    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
+    if (!scriptExecutionContext()-&gt;contentSecurityPolicy()-&gt;allowConnectToSource(m_url, scriptExecutionContext()-&gt;shouldBypassMainWorldContentSecurityPolicy())) {
+        if (m_async) {
+            setPendingActivity(this);
+            m_timeoutTimer.stop();
+            m_networkErrorTimer.startOneShot(0);
+        } else
+            ec = NETWORK_ERR;
+        return false;
+    }
+
</ins><span class="cx">     m_error = false;
</span><span class="cx">     return true;
</span><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>