Source/WebCore/ChangeLog

 12016-04-07 John Wilander <wilander@apple.com>
 2
 3 CSP: Block XHR when calling XMLHttpRequest.send() and throw network error.
 4 https://bugs.webkit.org/show_bug.cgi?id=153598
 5 <rdar://problem/24391483>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 No new tests. Changes to existing tests are sufficient.
 10
 11 * xml/XMLHttpRequest.cpp:
 12 (WebCore::XMLHttpRequest::open):
 13 (WebCore::XMLHttpRequest::initSend):
 14 Moved the CSP check from XMLHttpRequest::open() to XMLHttpRequest::initSend()
 15 and changed the error type from Security to Network.
 16 These changes are in conformance with connect-src of Content Security Policy Level 2.
 17 https://www.w3.org/TR/CSP2/#directive-connect-src (W3C Candidate Recommendation, 21 July 2015)
 18
1192016-04-05 Ada Chan <adachan@apple.com>
220
321 Rename TextTrackRepresentationiOS to TextTrackRepresentationCocoa and enable on Mac
199176

Source/WebCore/xml/XMLHttpRequest.cpp

@@void XMLHttpRequest::open(const String&
497497 return;
498498 }
499499
500  // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
501  if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(url, scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy())) {
502  // FIXME: Should this be throwing an exception?
503  ec = SECURITY_ERR;
504  return;
505  }
506 
507500 if (!async && scriptExecutionContext()->isDocument()) {
508501 if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
509502 logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests are disabled for this page.");

@@bool XMLHttpRequest::initSend(ExceptionC
573566 }
574567 ASSERT(!m_loader);
575568
 569 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
 570 // FIXME: We should only throw an exception for a synchronous request and should dispatch a DOM error event so as to make this CSP violation indistinguishable from a XHR network error. See <https://bugs.webkit.org/show_bug.cgi?id=156322>.
 571 if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url, scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy())) {
 572 ec = NETWORK_ERR;
 573 return false;
 574 }
 575
576576 m_error = false;
577577 return true;
578578}
199114

LayoutTests/ChangeLog

 12016-04-07 John Wilander <wilander@apple.com>
 2
 3 CSP: Block XHR when calling XMLHttpRequest.send() and throw network error.
 4 https://bugs.webkit.org/show_bug.cgi?id=153598
 5 <rdar://problem/24391483>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * fast/workers/resources/worker-inherits-csp-blocks-xhr.js:
 10 (catch):
 11 * fast/workers/worker-inherits-csp-blocks-xhr-expected.txt:
 12 Changed expected error from DOMException.SECURITY_ERR to DOMException.NETWORK_ERR.
 13 * http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt:
 14 * http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html:
 15 Now tests that XMLHttpRequest.send() is blocked if the URL voilates the connect-src directive in CSP.
 16 * http/tests/security/contentSecurityPolicy/resources/worker.php:
 17 Added two additional calls to XMLHttpRequest.send() to make existing tests work with the changes.
 18 * http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html:
 19 Added an additional call to XMLHttpRequest.send() to make existing test work with the changes.
 20 * http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt:
 21 * http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html:
 22 Added an additional call to XMLHttpRequest.send() to make existing test work with the changes.
 23 Refactored test mechnism with additional parameters for various test scenarios (see below).
 24 Now tests synchronous/asynchronous as well as same-origin/cross-origin in isolated worlds.
 25
1262016-04-06 Sam Weinig <sam@webkit.org>
227
328 window.CSS should be a constructor with static functions
199114

LayoutTests/fast/workers/worker-inherits-csp-blocks-xhr-expected.txt

11CONSOLE MESSAGE: Refused to connect to non-existent-file because it does not appear in the connect-src directive of the Content Security Policy.
22This 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 "connect-src 'none'"
33
4 PASS threw exception Error: SecurityError: DOM Exception 18.
 4PASS threw exception Error: NetworkError: DOM Exception 19.
199114

LayoutTests/fast/workers/resources/worker-inherits-csp-blocks-xhr.js

@@try {
77} catch (e) {
88 exception = e;
99}
10 // FIXME: We should be throwing a DOMException.NETWORK_ERR. See <https://bugs.webkit.org/show_bug.cgi?id=153598>.
11 var expectedExceptionCode = 18; // DOMException.SECURITY_ERR
 10
 11var expectedExceptionCode = 19; // DOMException.NETWORK_ERR
1212if (!exception)
1313 self.postMessage("FAIL should throw " + expectedExceptionCode + ". But did not throw an exception.");
1414else {
199114

LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt

11CONSOLE 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.
2 Pass
 2CONSOLE 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.
 3This tests that a Content Security Policy violation for an XHR is triggered when calling XMLHttpRequest.send().
 4
 5On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 6
 7
 8PASS xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/get.txt", true) did not throw exception.
 9PASS xhr.send() threw exception Error: NetworkError: DOM Exception 19.
 10PASS xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/get.txt", false) did not throw exception.
 11PASS xhr.send() threw exception Error: NetworkError: DOM Exception 19.
 12PASS successfullyParsed is true
 13
 14TEST COMPLETE
315
199114

LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked.html

22<html>
33<head>
44<meta http-equiv="Content-Security-Policy" content="connect-src http://127.0.0.1:8000">
5 <script>
6 if (window.testRunner)
7  testRunner.dumpAsText();
8 </script>
 5<script src="../../../resources/js-test-pre.js"></script>
96</head>
107<body>
11 <pre id="console"></pre>
128<script>
13 function log(msg)
14 {
15  document.getElementById("console").appendChild(document.createTextNode(msg + "\n"));
16 }
 9description("This tests that a Content Security Policy violation for an XHR is triggered when calling XMLHttpRequest.send().");
 10
 11var xhr = new XMLHttpRequest;
 12
 13shouldNotThrow('xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/get.txt", true)'); // Asynchronous request
 14shouldThrow("xhr.send()", "'Error: NetworkError: DOM Exception 19'");
1715
18 try {
19  var xhr = new XMLHttpRequest;
20  xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/get.txt", true);
21  log("Fail");
22 } catch(e) {
23  log("Pass");
24 }
 16shouldNotThrow('xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/get.txt", false)'); // Synchronous request
 17shouldThrow("xhr.send()", "'Error: NetworkError: DOM Exception 19'");
2518
2619</script>
 20<script src="/js-test-resources/js-test-post.js"></script>
2721</body>
2822</html>
199114

LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta.html

@@function log(msg)
1717
1818try {
1919 var xhr = new XMLHttpRequest;
20  xhr.open("GET", "http://127.0.0.1:8000/xmlhttprequest/resources/get.txt", true);
 20 xhr.open("GET", "http://127.0.0.1:8000/xmlhttprequest/resources/get.txt", true);
 21 xhr.send();
2122 log("Fail");
2223} catch(e) {
2324 log("Pass");
199114

LayoutTests/http/tests/security/contentSecurityPolicy/resources/worker.php

@@try {
6161try {
6262 var xhr = new XMLHttpRequest;
6363 xhr.open("GET", "http://127.0.0.1:8000/xmlhttprequest/resources/get.txt", true);
 64 xhr.send();
6465 postMessage("xhr allowed");
6566} catch(e) {
6667 postMessage("xhr blocked");

@@onconnect = function (e) {
118119try {
119120 var xhr = new XMLHttpRequest;
120121 xhr.open("GET", "http://127.0.0.1:8000/xmlhttprequest/resources/get.txt", true);
 122 xhr.send();
121123 postMessage("xhr allowed");
122124} catch(e) {
123125 postMessage("xhr blocked");
199114

LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr-expected.txt

1 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.
 1CONSOLE 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.
22Tests that isolated worlds can have XHRs that the page's CSP wouldn't allow.
33
44On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
55
66
7 XHR from main world
8 PASS: XHR.open threw an exception.
9 XHR from isolated world
10 PASS: XHR.open did not throw an exception.
 7Synchronous XHR same-origin from main world
 8PASS: XHR.send threw an exception.
 9Synchronous XHR same-origin from isolated world
 10PASS: XHR.send did not throw an exception.
 11Asynchronous XHR same-origin from isolated world
 12PASS: XHR.send did not throw an exception.
 13PASS: XHR.send received a load event.
 14Synchronous XHR cross-origin from isolated world
 15PASS: XHR.send did not throw an exception.
 16Asynchronous XHR cross-origin from isolated world
 17PASS: XHR.send did not throw an exception.
 18PASS: XHR.send received a load event.
1119PASS successfullyParsed is true
1220
1321TEST COMPLETE
199114

LayoutTests/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html

@@description('Tests that isolated worlds
1111
1212jsTestIsAsync = true;
1313
 14const SameOrigin = true;
 15const CrossOrigin = false;
 16const Asynchronous = true;
 17const Synchronous = false;
 18const ShouldBlock = true;
 19const ShouldNotBlock = false;
 20
1421var tests = [
1522 function() {
16  debug('XHR from main world');
17  xhr(true);
 23 debug('Synchronous XHR same-origin from main world');
 24 xhr({sameOrigin : true}, Synchronous, ShouldBlock);
 25 },
 26// FIXME: Add asynchronous same-origin test when https://bugs.webkit.org/show_bug.cgi?id=153155 is done.
 27// function() {
 28// debug('Asynchronous XHR same-origin from main world');
 29// xhr({sameOrigin : true}, Asynchronous, ShouldBlock);
 30// },
 31 function() {
 32 debug('Synchronous XHR same-origin from isolated world');
 33 invokeInWorld(1, xhr, SameOrigin, Synchronous, ShouldNotBlock);
1834 },
1935 function() {
20  debug('XHR from isolated world');
21  runTestInWorld(1, 'xhr', 'false');
 36 debug('Asynchronous XHR same-origin from isolated world');
 37 invokeInWorld(2, xhr, SameOrigin, Asynchronous, ShouldNotBlock);
2238 },
 39 function() {
 40 debug('Synchronous XHR cross-origin from isolated world');
 41 invokeInWorld(3, xhr, CrossOrigin, Synchronous, ShouldNotBlock);
 42 },
 43 function() {
 44 debug('Asynchronous XHR cross-origin from isolated world');
 45 invokeInWorld(4, xhr, CrossOrigin, Asynchronous, ShouldNotBlock);
 46 }
2347];
2448var currentTest = 0;
2549

@@if (window.testRunner) {
5276 testFailed('Test depends on LayoutTestController and must be run by DRT');
5377}
5478
55 function runTestInWorld(worldId, funcName, param)
56 {
57  testRunner.evaluateScriptInIsolatedWorld(
58  worldId, String(eval(funcName)) + "\n" + funcName + "(" + param + ");");
 79// This function will only successfully pass on JSON-stringifieable arguments such as numbers and strings to aNamedFunction
 80function invokeInWorld(worldId, aNamedFunction) {
 81 if (!aNamedFunction.name) {
 82 throw new Error("invokeInWorld requires a named function.");
 83 }
 84 var slice = Function.prototype.call.bind([].slice);
 85 var script = aNamedFunction.toString() + "; " + aNamedFunction.name + "(" + slice(arguments, 2).map(JSON.stringify).join(", ") + ");";
 86 testRunner.evaluateScriptInIsolatedWorld(worldId, script);
5987}
6088
61 function xhr(shouldBlock)
62 {
 89function xhr(isSameOrigin, isAsync, shouldBlock) {
6390 function debug(message) {
6491 window.postMessage(JSON.stringify({
6592 'type': 'debug',

@@function xhr(shouldBlock)
6895 '*');
6996 }
7097
71  var xhr = new XMLHttpRequest();
 98 var url = (isSameOrigin ? "http://127.0.0.1:8000/" : "http://localhost:8000/") + "xmlhttprequest/resources/access-control-basic-allow.cgi",
 99 xhr = new XMLHttpRequest(),
 100 asyncCallDone = false, finallyClauseDone = false;
 101 xhr.open('GET', url, isAsync);
 102
 103 if (isAsync) {
 104 xhr.addEventListener("load", function() {
 105 if (shouldBlock)
 106 debug('FAIL: XHR.send should not have received a load event.');
 107 else
 108 debug('PASS: XHR.send received a load event.');
 109
 110 if (finallyClauseDone)
 111 window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
 112 else
 113 asyncCallDone = true;
 114 });
 115
 116 // FIXME: Add event listener for error when https://bugs.webkit.org/show_bug.cgi?id=153155 is done.
 117 }
 118
72119 try {
73  xhr.open('GET', 'http://localhost:8000/security/isolatedWorld/resources/cross-origin-xhr.txt', true);
 120 xhr.send();
74121 if (shouldBlock)
75  debug('FAIL: XHR.open should have thrown an exception.');
 122 debug('FAIL: XHR.send should have thrown an exception.');
76123 else
77  debug('PASS: XHR.open did not throw an exception.');
 124 debug('PASS: XHR.send did not throw an exception.');
78125 } catch (e) {
79126 if (shouldBlock)
80  debug('PASS: XHR.open threw an exception.');
 127 debug('PASS: XHR.send threw an exception.');
81128 else
82  debug('FAIL: XHR.open should not have thrown an exception.');
 129 debug('FAIL: XHR.send should not have thrown an exception.');
83130 } finally {
84  window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
 131 if (!isAsync || asyncCallDone)
 132 window.postMessage(JSON.stringify({'type': 'test-done'}), '*');
 133 else
 134 finallyClauseDone = true;
85135 }
86136}
87 
88137</script>
89 
90138<script src="../../js-test-resources/js-test-post.js"></script>
91139</body>
92140</html>
199114