Title: [203899] trunk
Revision
203899
Author
commit-qu...@webkit.org
Date
2016-07-29 04:08:04 -0700 (Fri, 29 Jul 2016)

Log Message

CrossOrigin preflight checker should compute the right Access-Control-Request-Headers value
https://bugs.webkit.org/show_bug.cgi?id=160028

Patch by Youenn Fablet <you...@apple.com> on 2016-07-29
Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

* web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt:
* web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt:
* web-platform-tests/fetch/api/cors/cors-no-preflight.js:
(corsNoPreflight): fixing bugs in that test and adding new test to cover mime type checking that should ignore case.
* web-platform-tests/fetch/api/cors/cors-preflight-worker.html:
* web-platform-tests/fetch/api/cors/cors-preflight.html:
* web-platform-tests/fetch/api/cors/cors-preflight.js:
(corsPreflight): Adding safe headers to the request and checking more precisely sent Access-Control-Request-Headers header value.

Source/WebCore:

Covered by updated test.

Computing Access-Control-Request-Headers value according https://fetch.spec.whatwg.org/#cors-preflight-fetch:
- Remove safe headers
- lowercase header names
- sort lexicographically header names
The only difference is that we keep separating headers with ', ' instead of ',' as per the spec.
Also, some headers that might be safe are still marked as unsafe (DPR, Downlink...).

Moved setting of Origin header after preflighting, consistently with fetch spec.

* loader/CrossOriginAccessControl.cpp:
(WebCore::createAccessControlPreflightRequest): Implementing new computation of Access-Control-Request-Headers.
* loader/DocumentThreadableLoader.cpp:
(WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequest): Removing call to updateRequestForAccessControl (which sets Origin header value).
(WebCore::DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest): Adding call to updateRequestForAccessControl.
(WebCore::DocumentThreadableLoader::preflightSuccess): Ditto.
* platform/network/HTTPParsers.cpp:
(WebCore::isCrossOriginSafeRequestHeader): Helper routine to implement https://fetch.spec.whatwg.org/#cors-safelisted-request-header.
* platform/network/HTTPParsers.h:

LayoutTests:

Rebasing regular tests. Also updating skipped worker tests.

* platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt: Added.
* platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt: Added.
* platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt: Added.
* platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt: Added.
* platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt: Added.
* platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt: Added.
* platform/mac/TestExpectations:

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (203898 => 203899)


--- trunk/LayoutTests/ChangeLog	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/ChangeLog	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,3 +1,20 @@
+2016-07-29  Youenn Fablet  <you...@apple.com>
+
+        CrossOrigin preflight checker should compute the right Access-Control-Request-Headers value
+        https://bugs.webkit.org/show_bug.cgi?id=160028
+
+        Reviewed by Alex Christensen.
+
+        Rebasing regular tests. Also updating skipped worker tests.
+
+        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt: Added.
+        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt: Added.
+        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt: Added.
+        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt: Added.
+        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt: Added.
+        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt: Added.
+        * platform/mac/TestExpectations:
+
 2016-07-28  Youenn Fablet  <youe...@gmail.com>
 
         Binding generator should expose the visible interface name in error messages

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,3 +1,19 @@
+2016-07-29  Youenn Fablet  <you...@apple.com>
+
+        CrossOrigin preflight checker should compute the right Access-Control-Request-Headers value
+        https://bugs.webkit.org/show_bug.cgi?id=160028
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt:
+        * web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt:
+        * web-platform-tests/fetch/api/cors/cors-no-preflight.js:
+        (corsNoPreflight): fixing bugs in that test and adding new test to cover mime type checking that should ignore case.
+        * web-platform-tests/fetch/api/cors/cors-preflight-worker.html:
+        * web-platform-tests/fetch/api/cors/cors-preflight.html:
+        * web-platform-tests/fetch/api/cors/cors-preflight.js:
+        (corsPreflight): Adding safe headers to the request and checking more precisely sent Access-Control-Request-Headers header value.
+
 2016-07-28  Chris Dumez  <cdu...@apple.com>
 
         Add support for Element.getAttributeNames()

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,17 +1,17 @@
 
-FAIL Same domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Same domain different port [no-cors mode] 
 PASS Same domain different port [server forbid CORS] 
-FAIL Same domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
-FAIL Same domain different protocol different port [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Same domain different port [cors mode] 
+PASS Same domain different protocol different port [no-cors mode] 
 PASS Same domain different protocol different port [server forbid CORS] 
-FAIL Same domain different protocol different port [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
-FAIL Cross domain basic usage [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Same domain different protocol different port [cors mode] 
+PASS Cross domain basic usage [no-cors mode] 
 PASS Cross domain basic usage [server forbid CORS] 
-FAIL Cross domain basic usage [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
-FAIL Cross domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Cross domain basic usage [cors mode] 
+PASS Cross domain different port [no-cors mode] 
 PASS Cross domain different port [server forbid CORS] 
-FAIL Cross domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
-FAIL Cross domain different protocol [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain different port [cors mode] 
+PASS Cross domain different protocol [no-cors mode] 
 PASS Cross domain different protocol [server forbid CORS] 
-FAIL Cross domain different protocol [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain different protocol [cors mode] 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-filtering-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-filtering-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-filtering-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,20 +1,20 @@
 
-FAIL CORS filter on Cache-Control header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Content-Language header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Content-Type header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Expires header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Last-Modified header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Pragma header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Age header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Server header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Warning header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Content-Length header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Set-Cookie header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Set-Cookie2 header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Age header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Server header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Warning header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Content-Length header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Set-Cookie header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
-FAIL CORS filter on Set-Cookie2 header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
+PASS CORS filter on Cache-Control header 
+PASS CORS filter on Content-Language header 
+PASS CORS filter on Content-Type header 
+PASS CORS filter on Expires header 
+PASS CORS filter on Last-Modified header 
+PASS CORS filter on Pragma header 
+PASS CORS filter on Age header 
+PASS CORS filter on Server header 
+PASS CORS filter on Warning header 
+PASS CORS filter on Content-Length header 
+PASS CORS filter on Set-Cookie header 
+PASS CORS filter on Set-Cookie2 header 
+PASS CORS filter on Age header, header is exposed 
+PASS CORS filter on Server header, header is exposed 
+PASS CORS filter on Warning header, header is exposed 
+PASS CORS filter on Content-Length header, header is exposed 
+PASS CORS filter on Set-Cookie header, header is exposed 
+PASS CORS filter on Set-Cookie2 header, header is exposed 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -13,4 +13,5 @@
 PASS Cross domain [GET] [Content-Type: multipart/form-data] 
 PASS Cross domain [GET] [Content-Type: text/plain] 
 PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -13,4 +13,5 @@
 PASS Cross domain [GET] [Content-Type: multipart/form-data] 
 PASS Cross domain [GET] [Content-Type: text/plain] 
 PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight.js (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight.js	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight.js	2016-07-29 11:08:04 UTC (rev 203899)
@@ -15,7 +15,7 @@
     requestInit["headers"][headerName] = headerValue;
 
   promise_test(function(test) {
-    fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token).then(function(resp) {
+    return fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token).then(function(resp) {
       assert_equals(resp.status, 200, "Clean stash response's status is 200");
       return fetch(url + urlParameters, requestInit).then(function(resp) {
         assert_equals(resp.status, 200, "Response's status is 200");
@@ -28,8 +28,8 @@
 var host_info = get_host_info();
 
 corsNoPreflight("Cross domain basic usage [GET]", host_info.HTTP_REMOTE_ORIGIN, "GET");
-corsNoPreflight("Same domain different port [GET]", host_info.HTTP_ORIGIN_WITH_DIFFERENT_ORIGIN, "GET");
-corsNoPreflight("Cross domain different port [GET]", host_info.HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_ORIGIN, "GET");
+corsNoPreflight("Same domain different port [GET]", host_info.HTTP_ORIGIN_WITH_DIFFERENT_PORT, "GET");
+corsNoPreflight("Cross domain different port [GET]", host_info.HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_PORT, "GET");
 corsNoPreflight("Cross domain different protocol [GET]", host_info.HTTPS_REMOTE_ORIGIN, "GET");
 corsNoPreflight("Same domain different protocol different port [GET]", host_info.HTTPS_ORIGIN, "GET");
 corsNoPreflight("Cross domain [POST]", host_info.HTTP_REMOTE_ORIGIN, "POST");
@@ -41,5 +41,6 @@
 corsNoPreflight("Cross domain [GET] [Content-Type: multipart/form-data]", host_info.HTTP_REMOTE_ORIGIN, "GET" , "Content-Type", "multipart/form-data");
 corsNoPreflight("Cross domain [GET] [Content-Type: text/plain]", host_info.HTTP_REMOTE_ORIGIN, "GET" , "Content-Type", "text/plain");
 corsNoPreflight("Cross domain [GET] [Content-Type: text/plain;charset=utf-8]", host_info.HTTP_REMOTE_ORIGIN, "GET" , "Content-Type", "text/plain;charset=utf-8");
+corsNoPreflight("Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8]", host_info.HTTP_REMOTE_ORIGIN, "GET" , "Content-Type", "Text/Plain;charset=utf-8");
 
 done();

Deleted: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-redirect-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,12 +0,0 @@
-
-PASS Redirection 301 on preflight failed 
-PASS Redirection 301 after preflight failed 
-PASS Redirection 302 on preflight failed 
-PASS Redirection 302 after preflight failed 
-PASS Redirection 303 on preflight failed 
-PASS Redirection 303 after preflight failed 
-PASS Redirection 307 on preflight failed 
-PASS Redirection 307 after preflight failed 
-PASS Redirection 308 on preflight failed 
-PASS Redirection 308 after preflight failed 
-

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -7,10 +7,10 @@
 PASS CORS [PATCH], server refuses 
 PASS CORS [NEW], server allows 
 PASS CORS [NEW], server refuses 
-PASS CORS [GET] [x-test-header: allowed], server allows 
+FAIL CORS [GET] [x-test-header: allowed], server allows assert_equals: Access-Control-Allow-Headers value expected "x-test-header1" but got "referer,x-test-header1"
 PASS CORS [GET] [x-test-header: refused], server refuses 
-PASS CORS [GET] [several headers], server allows 
+FAIL CORS [GET] [several headers], server allows assert_equals: Access-Control-Allow-Headers value expected "content-type,x-test-header-a,x-test-header-b,x-test-header-c,x-test-header-d,x-test-header1,x-test-header2,x-test-header3" but got "content-type,referer,x-test-header-a,x-test-header-b,x-test-header-c,x-test-header-d,x-test-header1,x-test-header2,x-test-header3"
 PASS CORS [GET] [several headers], server refuses 
-PASS CORS [PUT] [several headers], server allows 
+FAIL CORS [PUT] [several headers], server allows assert_equals: Access-Control-Allow-Headers value expected "content-type,x-test-header-a,x-test-header-b,x-test-header-c,x-test-header-d,x-test-header1,x-test-header2,x-test-header3" but got "content-type,referer,x-test-header-a,x-test-header-b,x-test-header-c,x-test-header-d,x-test-header1,x-test-header2,x-test-header3"
 PASS CORS [PUT] [several headers], server refuses 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker.html (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker.html	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-worker.html	2016-07-29 11:08:04 UTC (rev 203899)
@@ -14,7 +14,7 @@
   </head>
   <body>
     <script>
-      fetch_tests_from_worker(new Worker("cors-preflight.js?pipe=sub"));
+      fetch_tests_from_worker(new Worker("cors-preflight.js"));
     </script>
   </body>
-</html>
\ No newline at end of file
+</html>

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.html (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.html	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.html	2016-07-29 11:08:04 UTC (rev 203899)
@@ -14,7 +14,8 @@
   </head>
   <body>
     <script src=""
+    <script src=""
     <script src=""
-    <script src=""
+    <script src=""
   </body>
-</html>
\ No newline at end of file
+</html>

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.js (203898 => 203899)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.js	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.js	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,15 +1,24 @@
 if (this.document === undefined) {
   importScripts("/resources/testharness.js");
+  importScripts("/common/utils.js");
   importScripts("../resources/utils.js");
-  importScripts("/common/utils.js");
+  importScripts("../resources/get-host-info.sub.js");
 }
 
+function headerNames(headers)
+{
+    let names = [];
+    for (let header of headers)
+        names.push(header[0].toLowerCase());
+    return names
+}
+
 /*
   Check preflight is done
   Control if server allows method and headers and check accordingly
   Check control access headers added by UA (for method and headers)
 */
-function corsPreflight(desc, corsUrl, method, allowed, headers) {
+function corsPreflight(desc, corsUrl, method, allowed, headers, safeHeaders) {
   return promise_test(function(test) {
     var uuid_token = token();
     return fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token).then(function(response) {
@@ -16,8 +25,12 @@
       var url = ""
       var urlParameters = "?token=" + uuid_token + "&max_age=0";
       var requestInit = {"mode": "cors", "method": method};
+      var requestHeaders = [];
       if (headers)
-        requestInit["headers"] = headers;
+        requestHeaders.push.apply(requestHeaders, headers);
+      if (safeHeaders)
+        requestHeaders.push.apply(requestHeaders, safeHeaders);
+      requestInit["headers"] = requestHeaders;
 
       if (allowed) {
         urlParameters += "&allow_methods=" + method;
@@ -26,20 +39,22 @@
           //Server will send back headers from Access-Control-Request-Headers in x-control-request-headers
           urlParameters += "&control_request_headers"
           //Make the server allow the headers
-          urlParameters += "&allow_headers="
-          urlParameters += headers.map(function (x) { return x[0]; }).join("%2C%20");
+          urlParameters += "&allow_headers=" + headerNames(headers).join("%20%2C");
         }
         return fetch(url + urlParameters, requestInit).then(function(resp) {
           assert_equals(resp.status, 200, "Response's status is 200");
           assert_equals(resp.headers.get("x-did-preflight"), "1", "Preflight request has been made");
           if (headers) {
-            var actualHeaders = resp.headers.get("x-control-request-headers").split(",");
+            var actualHeaders = resp.headers.get("x-control-request-headers").toLowerCase().split(",");
             for (var i in actualHeaders)
               actualHeaders[i] = actualHeaders[i].trim();
             for (var header of headers)
-              assert_in_array(header[0], actualHeaders, "Preflight asked permission for header: " + header);
+              assert_in_array(header[0].toLowerCase(), actualHeaders, "Preflight asked permission for header: " + header);
+
+            let accessControlAllowHeaders = headerNames(headers).sort().join(",");
+            assert_equals(resp.headers.get("x-control-request-headers").replace(new RegExp(" ", "g"), ""), accessControlAllowHeaders, "Access-Control-Allow-Headers value");
+            return fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token);
           }
-          return fetch(RESOURCES_DIR + "clean-stash.py?token=" + uuid_token);
         });
       } else {
         return promise_rejects(test, new TypeError(), fetch(url + urlParameters, requestInit)).then(function(){
@@ -50,7 +65,7 @@
   }, desc);
 }
 
-var corsUrl = "http://{{host}}:{{ports[http][1]}}" + dirname(location.pathname) + RESOURCES_DIR + "preflight.py";
+var corsUrl = get_host_info().HTTP_REMOTE_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "preflight.py";
 
 corsPreflight("CORS [DELETE], server allows", corsUrl, "DELETE", true);
 corsPreflight("CORS [DELETE], server refuses", corsUrl, "DELETE", false);
@@ -64,12 +79,25 @@
 corsPreflight("CORS [GET] [x-test-header: allowed], server allows", corsUrl, "GET", true, [["x-test-header1", "allowed"]]);
 corsPreflight("CORS [GET] [x-test-header: refused], server refuses", corsUrl, "GET", false, [["x-test-header1", "refused"]]);
 
-var headers = [["x-test-header1", "allowedOrRefused"],
-               ["x-test-header2", "allowedOrRefused"],
-               ["x-test-header3", "allowedOrRefused"]];
-corsPreflight("CORS [GET] [several headers], server allows", corsUrl, "GET", true, headers);
-corsPreflight("CORS [GET] [several headers], server refuses", corsUrl, "GET", false, headers);
-corsPreflight("CORS [PUT] [several headers], server allows", corsUrl, "PUT", true, headers);
-corsPreflight("CORS [PUT] [several headers], server refuses", corsUrl, "PUT", false, headers);
+var headers = [
+    ["x-test-header1", "allowedOrRefused"],
+    ["x-test-header2", "allowedOrRefused"],
+    ["X-test-header3", "allowedOrRefused"],
+    ["x-test-header-b", "allowedOrRefused"],
+    ["x-test-header-D", "allowedOrRefused"],
+    ["x-test-header-C", "allowedOrRefused"],
+    ["x-test-header-a", "allowedOrRefused"],
+    ["Content-Type", "allowedOrRefused"],
+];
+var safeHeaders= [
+    ["Accept", "*"],
+    ["Accept-Language", "bzh"],
+    ["Content-Language", "eu"],
+];
 
+corsPreflight("CORS [GET] [several headers], server allows", corsUrl, "GET", true, headers, safeHeaders);
+corsPreflight("CORS [GET] [several headers], server refuses", corsUrl, "GET", false, headers, safeHeaders);
+corsPreflight("CORS [PUT] [several headers], server allows", corsUrl, "PUT", true, headers, safeHeaders);
+corsPreflight("CORS [PUT] [several headers], server refuses", corsUrl, "PUT", false, headers, safeHeaders);
+
 done();

Copied: trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt (from rev 203898, trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt) (0 => 203899)


--- trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -0,0 +1,17 @@
+
+PASS Cross domain basic usage [GET] 
+PASS Same domain different port [GET] 
+PASS Cross domain different port [GET] 
+FAIL Cross domain different protocol [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Same domain different protocol different port [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain [POST] 
+PASS Cross domain [HEAD] 
+PASS Cross domain [GET] [Accept: */*] 
+PASS Cross domain [GET] [Accept-Language: fr] 
+PASS Cross domain [GET] [Content-Language: fr] 
+PASS Cross domain [GET] [Content-Type: application/x-www-form-urlencoded] 
+PASS Cross domain [GET] [Content-Type: multipart/form-data] 
+PASS Cross domain [GET] [Content-Type: text/plain] 
+PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
+

Copied: trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt (from rev 203898, trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt) (0 => 203899)


--- trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -0,0 +1,17 @@
+
+PASS Cross domain basic usage [GET] 
+PASS Same domain different port [GET] 
+PASS Cross domain different port [GET] 
+FAIL Cross domain different protocol [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Same domain different protocol different port [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain [POST] 
+PASS Cross domain [HEAD] 
+PASS Cross domain [GET] [Accept: */*] 
+PASS Cross domain [GET] [Accept-Language: fr] 
+PASS Cross domain [GET] [Content-Language: fr] 
+PASS Cross domain [GET] [Content-Type: application/x-www-form-urlencoded] 
+PASS Cross domain [GET] [Content-Type: multipart/form-data] 
+PASS Cross domain [GET] [Content-Type: text/plain] 
+PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
+

Modified: trunk/LayoutTests/platform/mac/TestExpectations (203898 => 203899)


--- trunk/LayoutTests/platform/mac/TestExpectations	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/platform/mac/TestExpectations	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1425,5 +1425,3 @@
 
 # rdar://problem/27475162
 [ Sierra+ ] compositing/video/poster.html [ Pass ImageOnlyFailure ]
-
-webkit.org/b/160056 imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight.html [ Pass Failure ]

Modified: trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt (203898 => 203899)


--- trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,16 +1,16 @@
 
-FAIL Same domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Same domain different port [no-cors mode] 
 PASS Same domain different port [server forbid CORS] 
-FAIL Same domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
+PASS Same domain different port [cors mode] 
 FAIL Same domain different protocol different port [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
 PASS Same domain different protocol different port [server forbid CORS] 
 FAIL Same domain different protocol different port [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
-FAIL Cross domain basic usage [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Cross domain basic usage [no-cors mode] 
 PASS Cross domain basic usage [server forbid CORS] 
-FAIL Cross domain basic usage [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
-FAIL Cross domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
+PASS Cross domain basic usage [cors mode] 
+PASS Cross domain different port [no-cors mode] 
 PASS Cross domain different port [server forbid CORS] 
-FAIL Cross domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
+PASS Cross domain different port [cors mode] 
 FAIL Cross domain different protocol [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
 PASS Cross domain different protocol [server forbid CORS] 
 FAIL Cross domain different protocol [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"

Copied: trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt (from rev 203898, trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt) (0 => 203899)


--- trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -0,0 +1,17 @@
+
+PASS Cross domain basic usage [GET] 
+PASS Same domain different port [GET] 
+PASS Cross domain different port [GET] 
+FAIL Cross domain different protocol [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Same domain different protocol different port [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain [POST] 
+PASS Cross domain [HEAD] 
+PASS Cross domain [GET] [Accept: */*] 
+PASS Cross domain [GET] [Accept-Language: fr] 
+PASS Cross domain [GET] [Content-Language: fr] 
+PASS Cross domain [GET] [Content-Type: application/x-www-form-urlencoded] 
+PASS Cross domain [GET] [Content-Type: multipart/form-data] 
+PASS Cross domain [GET] [Content-Type: text/plain] 
+PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
+

Copied: trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt (from rev 203898, trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt) (0 => 203899)


--- trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-no-preflight-worker-expected.txt	2016-07-29 11:08:04 UTC (rev 203899)
@@ -0,0 +1,17 @@
+
+PASS Cross domain basic usage [GET] 
+PASS Same domain different port [GET] 
+PASS Cross domain different port [GET] 
+FAIL Cross domain different protocol [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Same domain different protocol different port [GET] promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS Cross domain [POST] 
+PASS Cross domain [HEAD] 
+PASS Cross domain [GET] [Accept: */*] 
+PASS Cross domain [GET] [Accept-Language: fr] 
+PASS Cross domain [GET] [Content-Language: fr] 
+PASS Cross domain [GET] [Content-Type: application/x-www-form-urlencoded] 
+PASS Cross domain [GET] [Content-Type: multipart/form-data] 
+PASS Cross domain [GET] [Content-Type: text/plain] 
+PASS Cross domain [GET] [Content-Type: text/plain;charset=utf-8] 
+PASS Cross domain [GET] [Content-Type: Text/Plain;charset=utf-8] 
+

Modified: trunk/Source/WebCore/ChangeLog (203898 => 203899)


--- trunk/Source/WebCore/ChangeLog	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/Source/WebCore/ChangeLog	2016-07-29 11:08:04 UTC (rev 203899)
@@ -1,3 +1,31 @@
+2016-07-29  Youenn Fablet  <you...@apple.com>
+
+        CrossOrigin preflight checker should compute the right Access-Control-Request-Headers value
+        https://bugs.webkit.org/show_bug.cgi?id=160028
+
+        Reviewed by Alex Christensen.
+
+        Covered by updated test.
+
+        Computing Access-Control-Request-Headers value according https://fetch.spec.whatwg.org/#cors-preflight-fetch:
+        - Remove safe headers
+        - lowercase header names
+        - sort lexicographically header names
+        The only difference is that we keep separating headers with ', ' instead of ',' as per the spec.
+        Also, some headers that might be safe are still marked as unsafe (DPR, Downlink...).
+
+        Moved setting of Origin header after preflighting, consistently with fetch spec.
+
+        * loader/CrossOriginAccessControl.cpp:
+        (WebCore::createAccessControlPreflightRequest): Implementing new computation of Access-Control-Request-Headers.
+        * loader/DocumentThreadableLoader.cpp:
+        (WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequest): Removing call to updateRequestForAccessControl (which sets Origin header value).
+        (WebCore::DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest): Adding call to updateRequestForAccessControl.
+        (WebCore::DocumentThreadableLoader::preflightSuccess): Ditto.
+        * platform/network/HTTPParsers.cpp:
+        (WebCore::isCrossOriginSafeRequestHeader): Helper routine to implement https://fetch.spec.whatwg.org/#cors-safelisted-request-header.
+        * platform/network/HTTPParsers.h:
+
 2016-07-29  Frederic Wang  <fw...@igalia.com>
 
         Parse the operator text on the MathMLOperatorElement class.

Modified: trunk/Source/WebCore/loader/CrossOriginAccessControl.cpp (203898 => 203899)


--- trunk/Source/WebCore/loader/CrossOriginAccessControl.cpp	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/Source/WebCore/loader/CrossOriginAccessControl.cpp	2016-07-29 11:08:04 UTC (rev 203899)
@@ -116,19 +116,29 @@
     const HTTPHeaderMap& requestHeaderFields = request.httpHeaderFields();
 
     if (!requestHeaderFields.isEmpty()) {
+        Vector<String> unsafeHeaders;
+        for (const auto& headerField : requestHeaderFields.commonHeaders()) {
+            if (!isCrossOriginSafeRequestHeader(headerField.key, headerField.value))
+                unsafeHeaders.append(httpHeaderNameString(headerField.key).toStringWithoutCopying().convertToASCIILowercase());
+        }
+        for (const auto& headerField : requestHeaderFields.uncommonHeaders())
+            unsafeHeaders.append(headerField.key.convertToASCIILowercase());
+
+        std::sort(unsafeHeaders.begin(), unsafeHeaders.end(), WTF::codePointCompareLessThan);
+
         StringBuilder headerBuffer;
-        
+
         bool appendComma = false;
-        for (const auto& headerField : requestHeaderFields) {
+        for (const auto& headerField : unsafeHeaders) {
+            // FIXME: header names should be separated by 0x2C, without space.
             if (appendComma)
                 headerBuffer.appendLiteral(", ");
             else
                 appendComma = true;
-            
-            headerBuffer.append(headerField.key);
+
+            headerBuffer.append(headerField);
         }
-
-        preflightRequest.setHTTPHeaderField(HTTPHeaderName::AccessControlRequestHeaders, headerBuffer.toString().convertToASCIILowercase());
+        preflightRequest.setHTTPHeaderField(HTTPHeaderName::AccessControlRequestHeaders, headerBuffer.toString());
     }
 
     return preflightRequest;

Modified: trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp (203898 => 203899)


--- trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp	2016-07-29 11:08:04 UTC (rev 203899)
@@ -113,17 +113,14 @@
 {
     ASSERT(m_options.mode == FetchOptions::Mode::Cors);
 
-    auto crossOriginRequest = request;
-    updateRequestForAccessControl(crossOriginRequest, securityOrigin(), m_options.allowCredentials());
-
-    if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(crossOriginRequest.httpMethod(), crossOriginRequest.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight)
-        makeSimpleCrossOriginAccessRequest(crossOriginRequest);
+    if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight)
+        makeSimpleCrossOriginAccessRequest(request);
     else {
         m_simpleRequest = false;
-        if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), crossOriginRequest.url(), m_options.allowCredentials(), crossOriginRequest.httpMethod(), crossOriginRequest.httpHeaderFields()))
-            preflightSuccess(WTFMove(crossOriginRequest));
+        if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), request.url(), m_options.allowCredentials(), request.httpMethod(), request.httpHeaderFields()))
+            preflightSuccess(ResourceRequest(request));
         else
-            makeCrossOriginAccessRequestWithPreflight(WTFMove(crossOriginRequest));
+            makeCrossOriginAccessRequestWithPreflight(ResourceRequest(request));
     }
 }
 
@@ -138,7 +135,9 @@
         return;
     }
 
-    loadRequest(request, DoSecurityCheck);
+    auto crossOriginRequest = request;
+    updateRequestForAccessControl(crossOriginRequest, securityOrigin(), m_options.allowCredentials());
+    loadRequest(crossOriginRequest, DoSecurityCheck);
 }
 
 void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
@@ -334,7 +333,7 @@
 void DocumentThreadableLoader::preflightSuccess(ResourceRequest&& request)
 {
     ResourceRequest actualRequest(WTFMove(request));
-    actualRequest.setHTTPOrigin(securityOrigin().toString());
+    updateRequestForAccessControl(actualRequest, securityOrigin(), m_options.allowCredentials());
 
     m_preflightChecker = Nullopt;
 

Modified: trunk/Source/WebCore/platform/network/HTTPParsers.cpp (203898 => 203899)


--- trunk/Source/WebCore/platform/network/HTTPParsers.cpp	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.cpp	2016-07-29 11:08:04 UTC (rev 203899)
@@ -854,4 +854,22 @@
     return accessControlExposeHeaderSet.contains(name);
 }
 
+// Implements https://fetch.spec.whatwg.org/#cors-safelisted-request-header
+bool isCrossOriginSafeRequestHeader(HTTPHeaderName name, const String& value)
+{
+    switch (name) {
+    case HTTPHeaderName::Accept:
+    case HTTPHeaderName::AcceptLanguage:
+    case HTTPHeaderName::ContentLanguage:
+        return true;
+    case HTTPHeaderName::ContentType: {
+        String mimeType = extractMIMETypeFromMediaType(value);
+        return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");
+    }
+    default:
+        // FIXME: Should we also make safe other headers (DPR, Downlink, Save-Data...)? That would require validating their values.
+        return false;
+    }
 }
+
+}

Modified: trunk/Source/WebCore/platform/network/HTTPParsers.h (203898 => 203899)


--- trunk/Source/WebCore/platform/network/HTTPParsers.h	2016-07-29 09:58:41 UTC (rev 203898)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.h	2016-07-29 11:08:04 UTC (rev 203899)
@@ -109,6 +109,7 @@
 bool isSimpleHeader(const String& name, const String& value);
 bool isCrossOriginSafeHeader(HTTPHeaderName, const HTTPHeaderSet&);
 bool isCrossOriginSafeHeader(const String&, const HTTPHeaderSet&);
+bool isCrossOriginSafeRequestHeader(HTTPHeaderName, const String&);
 
 inline bool isHTTPSpace(UChar character)
 {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to