Access-Control-Expose-Headers parsed incorrectly
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Sep 2019 07:56:08 +0000 (07:56 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Sep 2019 07:56:08 +0000 (07:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172357

Patch by Rob Buis <rbuis@igalia.com> on 2019-09-17
Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

Import cors/access-control-expose-headers-parsing.window.js and adjust it
so it fetches a remote url, since subdomains do not work for localhost.

* web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.
* web-platform-tests/cors/access-control-expose-headers-parsing.window.html: Added.
* web-platform-tests/cors/access-control-expose-headers-parsing.window.js:
(runTests):
(exposeTest): Deleted.
* web-platform-tests/cors/allow-headers-expected.txt: Removed.
* web-platform-tests/cors/allow-headers.htm: Removed.
* web-platform-tests/cors/resources/access-control-expose-headers-parsing-2.asis: Removed.
* web-platform-tests/cors/resources/access-control-expose-headers-parsing.asis: Removed.
* web-platform-tests/cors/resources/access-control-expose-headers.json: Added.
* web-platform-tests/cors/resources/expose-headers.py: Added.
(main):

Source/WebCore:

When parsing the list, strip HTTP spaces and verify
that the list items are valid HTTP tokens.

Behavior matches Firefox and Chrome.

Test: imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.html

* platform/network/HTTPParsers.h:
(WebCore::addToAccessControlAllowList):
(WebCore::parseAccessControlAllowList):

LayoutTests:

Add Mac/iOS expectations.

* platform/ios/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.
* platform/mac/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@249946 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.js
LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers-expected.txt [deleted file]
LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers.htm [deleted file]
LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing-2.asis [deleted file]
LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing.asis [deleted file]
LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers.json [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/cors/resources/expose-headers.py [new file with mode: 0644]
LayoutTests/platform/ios/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/network/HTTPParsers.h

index 4f242bd..2d0e432 100644 (file)
@@ -1,3 +1,15 @@
+2019-09-17  Rob Buis  <rbuis@igalia.com>
+
+        Access-Control-Expose-Headers parsed incorrectly
+        https://bugs.webkit.org/show_bug.cgi?id=172357
+
+        Reviewed by Youenn Fablet.
+
+        Add Mac/iOS expectations.
+
+        * platform/ios/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.
+        * platform/mac/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.
+
 2019-09-16  Alex Christensen  <achristensen@webkit.org>
 
         Remove "gopher" from list of special schemes in URLParser
index aad8503..237a45f 100644 (file)
@@ -1,3 +1,26 @@
+2019-09-17  Rob Buis  <rbuis@igalia.com>
+
+        Access-Control-Expose-Headers parsed incorrectly
+        https://bugs.webkit.org/show_bug.cgi?id=172357
+
+        Reviewed by Youenn Fablet.
+
+        Import cors/access-control-expose-headers-parsing.window.js and adjust it
+        so it fetches a remote url, since subdomains do not work for localhost.
+
+        * web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt: Added.
+        * web-platform-tests/cors/access-control-expose-headers-parsing.window.html: Added.
+        * web-platform-tests/cors/access-control-expose-headers-parsing.window.js:
+        (runTests):
+        (exposeTest): Deleted.
+        * web-platform-tests/cors/allow-headers-expected.txt: Removed.
+        * web-platform-tests/cors/allow-headers.htm: Removed.
+        * web-platform-tests/cors/resources/access-control-expose-headers-parsing-2.asis: Removed.
+        * web-platform-tests/cors/resources/access-control-expose-headers-parsing.asis: Removed.
+        * web-platform-tests/cors/resources/access-control-expose-headers.json: Added.
+        * web-platform-tests/cors/resources/expose-headers.py: Added.
+        (main):
+
 2019-09-17  Youenn Fablet  <youenn@apple.com>
 
         Content-Type should be preserved on responses created from DOMCache
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt
new file mode 100644 (file)
index 0000000..715a2f2
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS Loading JSON… 
+PASS Parsing: access-control-expose-headers%3A%20BB-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11! 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20no%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0C 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8' 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8'%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%22bb-8%22%2Cbb-8 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.html b/LayoutTests/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.html
new file mode 100644 (file)
index 0000000..2382913
--- /dev/null
@@ -0,0 +1 @@
+<!-- This file is required for WebKit test infrastructure to run the templated test -->
\ No newline at end of file
index 58e7395..1b5afdf 100644 (file)
@@ -1,13 +1,17 @@
-function exposeTest(resource, desc) {
-  const url = new URL("resources/" + resource, location.href).href.replace("://", "://élève.");
+// META: script=/common/get-host-info.sub.js
 
-  promise_test(() => {
-    return fetch(url).then(res => {
-      assert_equals(res.headers.get("content-language"), "sure")
-      assert_equals(res.headers.get("x-custom"), null);
-    })
-  }, "Access-Control-Expose-Headers parsing: " + desc);
-}
+promise_test(() => fetch("resources/access-control-expose-headers.json").then(res => res.json()).then(runTests), "Loading JSON…");
 
-exposeTest("access-control-expose-headers-parsing.asis", "#1");
-exposeTest("access-control-expose-headers-parsing-2.asis", "#2")
+function runTests(allTestData) {
+  allTestData.forEach(testData => {
+    const encodedInput = encodeURIComponent(testData.input);
+    promise_test(() => {
+      const relativeURL = "cors/resources/expose-headers.py?expose=" + encodedInput,
+            url = new URL(relativeURL, get_host_info().HTTP_REMOTE_ORIGIN);
+      return fetch(url).then(res => {
+        assert_equals(res.headers.get("content-language"), "mkay");
+        assert_equals(res.headers.get("bb-8"), (testData.exposed ? "hey" : null));
+      });
+    }, "Parsing: " + encodedInput);
+  })
+}
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers-expected.txt
deleted file mode 100644 (file)
index f396052..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%20*%20%20
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%09*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%20http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%20http%3A%2F%2Flocalhost%3A8800%20%20%20%09%20
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%09http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Fwww1.localhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=ftp%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3Alocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=localhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%3F
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%2F
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%20%2F
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%23
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%2523
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%3A80
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%2C%20*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%00
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=HTTP%3A%2F%2FLOCALHOST%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=HTTP%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=-
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=**
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%00*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*%00
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%27*%27
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%22*%22
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*%20*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*http%3A%2F%2F*
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*%20http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=*%2C%20http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=%00http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=null%20http%3A%2F%2Flocalhost%3A8800
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Fexample.net
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=null
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%2Fcors%2Fallow-headers.htm
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Flocalhost%3A8800%2Fcors%2F
-Blocked access to external URL http://www1.localhost:8800/cors//resources/cors-makeheader.py?origin=http%3A%2F%2Fwww1.localhost%3A8800%2Fcors%2F
-Access-Control-Allow-Headers handling
-
-
-FAIL Allow origin: *  A network error occurred.
-FAIL Allow origin: _*__  A network error occurred.
-FAIL Allow origin: [tab]*  A network error occurred.
-FAIL Allow origin: http://localhost:8800  A network error occurred.
-FAIL Allow origin: _http://localhost:8800  A network error occurred.
-FAIL Allow origin: _http://localhost:8800___[tab]_  A network error occurred.
-FAIL Allow origin: [tab]http://localhost:8800  A network error occurred.
-PASS Disallow origin: http://www1.localhost:8800 
-PASS Disallow origin: //localhost:8800 
-PASS Disallow origin: ://localhost:8800 
-PASS Disallow origin: ftp://localhost:8800 
-PASS Disallow origin: http:://localhost:8800 
-PASS Disallow origin: http:/localhost:8800 
-PASS Disallow origin: http:localhost:8800 
-PASS Disallow origin: localhost:8800 
-PASS Disallow origin: http://localhost:8800? 
-PASS Disallow origin: http://localhost:8800/ 
-PASS Disallow origin: http://localhost:8800 / 
-PASS Disallow origin: http://localhost:8800# 
-PASS Disallow origin: http://localhost:8800%23 
-PASS Disallow origin: http://localhost:8800:80 
-PASS Disallow origin: http://localhost:8800, * 
-PASS Disallow origin: http://localhost:8800\0 
-PASS Disallow origin: HTTP://LOCALHOST:8800 
-PASS Disallow origin: HTTP://localhost:8800 
-PASS Disallow origin: - 
-PASS Disallow origin: ** 
-PASS Disallow origin: \0* 
-PASS Disallow origin: *\0 
-PASS Disallow origin: '*' 
-PASS Disallow origin: "*" 
-PASS Disallow origin: * * 
-PASS Disallow origin: *http://* 
-PASS Disallow origin: *http://localhost:8800 
-PASS Disallow origin: * http://localhost:8800 
-PASS Disallow origin: *, http://localhost:8800 
-PASS Disallow origin: \0http://localhost:8800 
-PASS Disallow origin: null http://localhost:8800 
-PASS Disallow origin: http://example.net 
-PASS Disallow origin: null 
-PASS Disallow origin:  
-PASS Disallow origin: http://localhost:8800/cors/allow-headers.htm 
-PASS Disallow origin: http://localhost:8800/cors/ 
-PASS Disallow origin: http://www1.localhost:8800/cors/ 
-
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers.htm b/LayoutTests/imported/w3c/web-platform-tests/cors/allow-headers.htm
deleted file mode 100644 (file)
index 8f25f3e..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Access-Control-Allow-Headers handling</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=support.js?pipe=sub></script>
-
-<h1>Access-Control-Allow-Headers handling</h1>
-
-<div id=log></div>
-
-<script>
-
-/*
- * Origin header
- */
-function shouldPass(origin) {
-    test(function () {
-        var client = new XMLHttpRequest()
-        client.open('GET', CROSSDOMAIN
-                            + '/resources/cors-makeheader.py?origin='
-                            + encodeURIComponent(origin),
-                    false)
-        client.send()
-        r = JSON.parse(client.response)
-        var host = location.protocol + "//" + location.host
-        assert_equals(r['origin'], host, 'Request Origin: should be ' + host)
-    }, 'Allow origin: ' + origin.replace(/\t/g, "[tab]").replace(/ /g, '_'));
-}
-
-shouldPass('*');
-shouldPass(' *  ');
-shouldPass('   *');
-shouldPass(location.protocol + "//" + location.host);
-shouldPass(" "+location.protocol + "//" + location.host);
-shouldPass(" "+location.protocol + "//" + location.host + "     ");
-shouldPass("   "+location.protocol + "//" + location.host);
-
-
-function shouldFail(origin) {
-    test(function () {
-        var client = new XMLHttpRequest()
-        client.open('GET', CROSSDOMAIN
-                            + '/resources/cors-makeheader.py?origin='
-                            + encodeURIComponent(origin),
-                    false)
-        assert_throws("NetworkError", function() { client.send() }, 'send')
-    }, 'Disallow origin: ' + origin.replace('\0', '\\0'));
-}
-
-shouldFail(location.protocol + "//" + SUBDOMAIN + "." + location.host)
-shouldFail("//" + location.host)
-shouldFail("://" + location.host)
-shouldFail("ftp://" + location.host)
-shouldFail("http:://" + location.host)
-shouldFail("http:/" + location.host)
-shouldFail("http:" + location.host)
-shouldFail(location.host)
-shouldFail(location.protocol + "//" + location.host + "?")
-shouldFail(location.protocol + "//" + location.host + "/")
-shouldFail(location.protocol + "//" + location.host + " /")
-shouldFail(location.protocol + "//" + location.host + "#")
-shouldFail(location.protocol + "//" + location.host + "%23")
-shouldFail(location.protocol + "//" + location.host + ":80")
-shouldFail(location.protocol + "//" + location.host + ", *")
-shouldFail(location.protocol + "//" + location.host + "\0")
-shouldFail((location.protocol + "//" + location.host).toUpperCase())
-shouldFail(location.protocol.toUpperCase() + "//" + location.host)
-shouldFail("-")
-shouldFail("**")
-shouldFail("\0*")
-shouldFail("*\0")
-shouldFail("'*'")
-shouldFail('"*"')
-shouldFail("* *")
-shouldFail("*" + location.protocol + "//" + "*")
-shouldFail("*" + location.protocol + "//" + location.host)
-shouldFail("* " + location.protocol + "//" + location.host)
-shouldFail("*, " + location.protocol + "//" + location.host)
-shouldFail("\0" + location.protocol + "//" + location.host)
-shouldFail("null " + location.protocol + "//" + location.host)
-shouldFail('http://example.net')
-shouldFail('null')
-shouldFail('')
-shouldFail(location.href)
-shouldFail(dirname(location.href))
-shouldFail(CROSSDOMAIN)
-
-</script>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing-2.asis b/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing-2.asis
deleted file mode 100644 (file)
index 9628a67..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-HTTP/1.1 200 OK
-Access-Control-Allow-Origin: *
-Access-Control-Expose-Headers: not valid
-Access-Control-Expose-Headers: x-custom
-X-Custom: test
-Content-Language: sure
-
-TEST
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing.asis b/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers-parsing.asis
deleted file mode 100644 (file)
index 7a4b2a1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-HTTP/1.1 200 OK
-Access-Control-Allow-Origin: *
-Access-Control-Expose-Headers: not valid, x-custom
-X-Custom: test
-Content-Language: sure
-
-TEST
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers.json b/LayoutTests/imported/w3c/web-platform-tests/cors/resources/access-control-expose-headers.json
new file mode 100644 (file)
index 0000000..e8915f7
--- /dev/null
@@ -0,0 +1,62 @@
+[
+  {
+    "input": "access-control-expose-headers: BB-8",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8,,@#$#%%&^&^*()()11!",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8, no no",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: @#$#%%&^&^*()()11!,bb-8",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no no",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: no\r\nAccess-Control-Expose-Headers: bb-8",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers:\r\nAccess-Control-Expose-Headers: bb-8",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers: ,bb-8",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8\u000C",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8\u000B",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: bb-8\u000B,bb-8",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: 'bb-8'",
+    "exposed": false
+  },
+  {
+    "input": "Access-Control-Expose-Headers: 'bb-8',bb-8",
+    "exposed": true
+  },
+  {
+    "input": "Access-Control-Expose-Headers: \"bb-8\",bb-8",
+    "exposed": false
+  }
+]
diff --git a/LayoutTests/imported/w3c/web-platform-tests/cors/resources/expose-headers.py b/LayoutTests/imported/w3c/web-platform-tests/cors/resources/expose-headers.py
new file mode 100644 (file)
index 0000000..c350b3b
--- /dev/null
@@ -0,0 +1,10 @@
+def main(request, response):
+    response.add_required_headers = False
+    output =  "HTTP/1.1 221 ALL YOUR BASE BELONG TO H1\r\n"
+    output += "Access-Control-Allow-Origin: *\r\n"
+    output += "BB-8: hey\r\n"
+    output += "Content-Language: mkay\r\n"
+    output += request.GET.first("expose") + "\r\n"
+    output += "\r\n"
+    response.writer.write(output)
+    response.close_connection = True
diff --git a/LayoutTests/platform/ios/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt b/LayoutTests/platform/ios/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt
new file mode 100644 (file)
index 0000000..799071a
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS Loading JSON… 
+PASS Parsing: access-control-expose-headers%3A%20BB-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11! 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20no%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%2Cbb-8 
+FAIL Parsing: Access-Control-Expose-Headers%3A%20bb-8%0C assert_equals: expected (object) null but got (string) "hey"
+FAIL Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B assert_equals: expected (object) null but got (string) "hey"
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8' 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8'%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%22bb-8%22%2Cbb-8 
+
diff --git a/LayoutTests/platform/mac/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt b/LayoutTests/platform/mac/imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window-expected.txt
new file mode 100644 (file)
index 0000000..799071a
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS Loading JSON… 
+PASS Parsing: access-control-expose-headers%3A%20BB-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11! 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%2C%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0D%0AAccess-Control-Expose-Headers%3A%20no%20no 
+PASS Parsing: Access-Control-Expose-Headers%3A%20no%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%0D%0AAccess-Control-Expose-Headers%3A%20bb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%2Cbb-8 
+FAIL Parsing: Access-Control-Expose-Headers%3A%20bb-8%0C assert_equals: expected (object) null but got (string) "hey"
+FAIL Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B assert_equals: expected (object) null but got (string) "hey"
+PASS Parsing: Access-Control-Expose-Headers%3A%20bb-8%0B%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8' 
+PASS Parsing: Access-Control-Expose-Headers%3A%20'bb-8'%2Cbb-8 
+PASS Parsing: Access-Control-Expose-Headers%3A%20%22bb-8%22%2Cbb-8 
+
index a0b9eaa..24d8dbe 100644 (file)
@@ -1,3 +1,21 @@
+2019-09-17  Rob Buis  <rbuis@igalia.com>
+
+        Access-Control-Expose-Headers parsed incorrectly
+        https://bugs.webkit.org/show_bug.cgi?id=172357
+
+        Reviewed by Youenn Fablet.
+
+        When parsing the list, strip HTTP spaces and verify
+        that the list items are valid HTTP tokens.
+
+        Behavior matches Firefox and Chrome.
+
+        Test: imported/w3c/web-platform-tests/cors/access-control-expose-headers-parsing.window.html
+
+        * platform/network/HTTPParsers.h:
+        (WebCore::addToAccessControlAllowList):
+        (WebCore::parseAccessControlAllowList):
+
 2019-09-17  Youenn Fablet  <youenn@apple.com>
 
         Content-Type should be preserved on responses created from DOMCache
index 82c3a15..ef3c1f5 100644 (file)
@@ -124,25 +124,30 @@ inline StringView stripLeadingAndTrailingHTTPSpaces(StringView string)
 }
 
 template<class HashType>
-void addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set)
+bool addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set)
 {
     StringImpl* stringImpl = string.impl();
     if (!stringImpl)
-        return;
+        return true;
 
     // Skip white space from start.
-    while (start <= end && isSpaceOrNewline((*stringImpl)[start]))
+    while (start <= end && isHTTPSpace((*stringImpl)[start]))
         ++start;
 
     // only white space
     if (start > end)
-        return;
+        return true;
 
     // Skip white space from end.
-    while (end && isSpaceOrNewline((*stringImpl)[end]))
+    while (end && isHTTPSpace((*stringImpl)[end]))
         --end;
 
-    set.add(string.substring(start, end - start + 1));
+    auto token = string.substring(start, end - start + 1);
+    if (!isValidHTTPToken(token))
+        return false;
+
+    set.add(WTFMove(token));
+    return true;
 }
 
 template<class HashType = DefaultHash<String>::Hash>
@@ -152,12 +157,16 @@ HashSet<String, HashType> parseAccessControlAllowList(const String& string)
     unsigned start = 0;
     size_t end;
     while ((end = string.find(',', start)) != notFound) {
-        if (start != end)
-            addToAccessControlAllowList(string, start, end - 1, set);
+        if (start != end) {
+            if (!addToAccessControlAllowList(string, start, end - 1, set))
+                return { };
+        }
         start = end + 1;
     }
-    if (start != string.length())
-        addToAccessControlAllowList(string, start, string.length() - 1, set);
+    if (start != string.length()) {
+        if (!addToAccessControlAllowList(string, start, string.length() - 1, set))
+            return { };
+    }
     return set;
 }