Changeset 203815 in webkit


Ignore:
Timestamp:
Jul 28, 2016 1:30:14 AM (8 years ago)
Author:
commit-queue@webkit.org
Message:

Compute fetch response type in case of cross-origin requests
https://bugs.webkit.org/show_bug.cgi?id=158565

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

LayoutTests/imported/w3c:

Rebasing fetch API tests as filtering is now done.
Rebasing XHR tests as console messages are no longer available when trying to access non-exposed headers.

  • web-platform-tests/XMLHttpRequest/getresponseheader-cookies-and-more-expected.txt:
  • web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
  • web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
  • web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
  • web-platform-tests/fetch/api/cors/cors-filtering-expected.txt:
  • web-platform-tests/fetch/api/request/request-cache-expected.txt:

Source/WebCore:

Covered by rebased tests.

Implementing Response filtering based on Response tainting in ResourceResponse.
Refactoring code in FetchHeaders and CrossOriginAccessControl.cpp accordingly.

Computing response tainting in SubresourceLoader for all resources.
This is used by DocumentThreadableLoader which now filters responses accordingly for all its clients including fetch and XHR.

Response tainting notably allows computing the response type and filtering out headers in case of cross origin responses.

Removing the filtering implemented in XMLHttpRequest as this is done before it gets access to the headers.
This is triggering some rebasing in the XHR tests as error messages triggered by trying to access unsafe headers no longer happen.

This filtering currently requires creating a new ResourceResponse object from the one sent from CachedResource.
This is done so as the same ResourceResponse may be reused accross loads and may be filtered differently by given to two different DocumentThreadableLoader
This can be mitigated in the future by changing ThreadableLoaderClient API to pass a ResourceResponse&&.

  • Modules/fetch/FetchHeaders.cpp: Moving header checking in HTTParsers.h/.cpp

(WebCore::isForbiddenHeaderName): Deleted.
(WebCore::isForbiddenResponseHeaderName): Deleted.
(WebCore::isSimpleHeader): Deleted.

  • loader/CrossOriginAccessControl.cpp:

(WebCore::parseAccessControlExposeHeadersAllowList): Deleted.

  • loader/CrossOriginAccessControl.h: Moving header checking in HTTParsers.h/.cpp
  • loader/DocumentThreadableLoader.cpp:

(WebCore::DocumentThreadableLoader::responseReceived):
(WebCore::DocumentThreadableLoader::didReceiveResponse): Doing response filtering. Since underlying loaders are
not yet aware that fetch mode may be cors (it is always no-cors currently), the tainting needs to be updated.
(WebCore::DocumentThreadableLoader::loadRequest): Computing response tainting in case of synchronous calls to ensure headers are filtered for synchronous XHR.

  • loader/DocumentThreadableLoader.h:
  • loader/SubresourceLoader.cpp:

(WebCore::SubresourceLoader::init): Getting origin from its resource and setting response tainting accordingly
(WebCore::SubresourceLoader::willSendRequestInternal): Always calling checkRedirectionCrossOriginAccessControl
to ensure response tainting is computed, even for no-cors resources.
(WebCore::SubresourceLoader::didReceiveResponse):
(WebCore::SubresourceLoader::checkRedirectionCrossOriginAccessControl): Computing response tainting in case of redirection.

  • loader/SubresourceLoader.h:
  • loader/cache/CachedResource.cpp:

(WebCore::CachedResource::load): Computing resource origin from the HTTP headers or from the document if none is
set in the HTTP headers.
(WebCore::CachedResource::setCrossOrigin): Helper routine to set response tainting.
(WebCore::CachedResource::isCrossOrigin): Helper routine to know whether resource is cross origin
(WebCore::CachedResource::isClean):
(WebCore::CachedResource::setResponse): Removing m_responseType

  • loader/cache/CachedResource.h:

(WebCore::CachedResource::responseTainting):
(WebCore::CachedResource::origin):
(WebCore::CachedResource::setOpaqueRedirect): Deleted.

  • platform/network/HTTPParsers.cpp: Implementing safe response header checking

(WebCore::parseAccessControlExposeHeadersAllowList):
(WebCore::isForbiddenHeaderName):
(WebCore::isForbiddenResponseHeaderName):
(WebCore::isSimpleHeader):
(WebCore::isCrossOriginSafeHeader):

  • platform/network/HTTPParsers.h:
  • platform/network/ResourceRequestBase.cpp:

(WebCore::ResourceRequestBase::hasHTTPOrigin): Added.
(WebCore::ResourceRequestBase::clearHTTPOrigin):

  • platform/network/ResourceRequestBase.h:
  • platform/network/ResourceResponseBase.cpp: Implementation of response filtering.

(WebCore::ResourceResponseBase::filterResponse):

  • platform/network/ResourceResponseBase.h:
  • xml/XMLHttpRequest.cpp:

(WebCore::isSetCookieHeader): Deleted.
(WebCore::XMLHttpRequest::getAllResponseHeaders): Removing header filtering since DocumentThreadableLoader does it.
(WebCore::XMLHttpRequest::getResponseHeader): Ditto.

LayoutTests:

Rebasing fetch API tests as filtering is now done.
Rebasing XHR tests as console messages are no longer available when trying to access non-exposed headers.

  • http/tests/xmlhttprequest/access-control-basic-whitelist-response-headers-expected.txt:
  • http/tests/xmlhttprequest/access-control-response-with-expose-headers-expected.txt:
  • http/tests/xmlhttprequest/get-dangerous-headers-expected.txt:
  • http/tests/xmlhttprequest/getResponseHeader-expected.txt:
  • platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
  • platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
  • platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
  • platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
  • platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
  • platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
Location:
trunk
Files:
35 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r203810 r203815  
     12016-07-28  Youenn Fablet  <youenn@apple.com>
     2
     3        Compute fetch response type in case of cross-origin requests
     4        https://bugs.webkit.org/show_bug.cgi?id=158565
     5
     6        Reviewed by Alex Christensen.
     7
     8        Rebasing fetch API tests as filtering is now done.
     9        Rebasing XHR tests as console messages are no longer available when trying to access non-exposed headers.
     10
     11        * http/tests/xmlhttprequest/access-control-basic-whitelist-response-headers-expected.txt:
     12        * http/tests/xmlhttprequest/access-control-response-with-expose-headers-expected.txt:
     13        * http/tests/xmlhttprequest/get-dangerous-headers-expected.txt:
     14        * http/tests/xmlhttprequest/getResponseHeader-expected.txt:
     15        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
     16        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
     17        * platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
     18        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
     19        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
     20        * platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
     21
    1222016-07-27  Ryan Haddad  <ryanhaddad@apple.com>
    223
  • trunk/LayoutTests/http/tests/xmlhttprequest/access-control-basic-whitelist-response-headers-expected.txt

    r178527 r203815  
    1 CONSOLE MESSAGE: line 25: Refused to get unsafe header "x-webkit"
    21PASS: Response header cache-control allowed.
    32PASS: Response header content-language allowed.
  • trunk/LayoutTests/http/tests/xmlhttprequest/access-control-response-with-expose-headers-expected.txt

    r178527 r203815  
    1 CONSOLE MESSAGE: line 1: Refused to get unsafe header "X-TEST"
    21Test for bug 41210: Cross Origin XMLHttpRequest can not expose headers indicated in Access-Control-Expose-Headers HTTP Response Header.
    32
  • trunk/LayoutTests/http/tests/xmlhttprequest/get-dangerous-headers-expected.txt

    r178527 r203815  
    1 CONSOLE MESSAGE: line 15: Refused to get unsafe header "Set-Cookie"
    2 CONSOLE MESSAGE: line 17: Refused to get unsafe header "set-cookie"
    3 CONSOLE MESSAGE: line 19: Refused to get unsafe header "Set-Cookie2"
    4 CONSOLE MESSAGE: line 21: Refused to get unsafe header "set-cookie2"
    51Test that getResponseHeader and getAllResponseHeaders cannot be used to get the cookie header fields.
    62
  • trunk/LayoutTests/http/tests/xmlhttprequest/getResponseHeader-expected.txt

    r178527 r203815  
    1 CONSOLE MESSAGE: line 1: Refused to get unsafe header "SeT-COoKie"
    2 CONSOLE MESSAGE: line 1: Refused to get unsafe header "sEt-coOkIE2"
    3 CONSOLE MESSAGE: line 1: Refused to get unsafe header "SeT-COoKie"
    4 CONSOLE MESSAGE: line 1: Refused to get unsafe header "sEt-coOkIE2"
    5 CONSOLE MESSAGE: line 1: Refused to get unsafe header "SeT-COoKie"
    6 CONSOLE MESSAGE: line 1: Refused to get unsafe header "sEt-coOkIE2"
    71Test the required behavior of XMLHttpRequest.getResponseHeader()
    82
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r203806 r203815  
     12016-07-28  Youenn Fablet  <youenn@apple.com>
     2
     3        Compute fetch response type in case of cross-origin requests
     4        https://bugs.webkit.org/show_bug.cgi?id=158565
     5
     6        Reviewed by Alex Christensen.
     7
     8        Rebasing fetch API tests as filtering is now done.
     9        Rebasing XHR tests as console messages are no longer available when trying to access non-exposed headers.
     10
     11        * web-platform-tests/XMLHttpRequest/getresponseheader-cookies-and-more-expected.txt:
     12        * web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt:
     13        * web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt:
     14        * web-platform-tests/fetch/api/cors/cors-basic-expected.txt:
     15        * web-platform-tests/fetch/api/cors/cors-filtering-expected.txt:
     16        * web-platform-tests/fetch/api/request/request-cache-expected.txt:
     17
    1182016-07-27  Chris Dumez  <cdumez@apple.com>
    219
  • trunk/LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/getresponseheader-cookies-and-more-expected.txt

    r191546 r203815  
    1 CONSOLE MESSAGE: line 23: Refused to get unsafe header "set-cookie"
    2 CONSOLE MESSAGE: line 24: Refused to get unsafe header "set-cookie2"
    3 CONSOLE MESSAGE: line 23: Refused to get unsafe header "set-cookie"
    4 CONSOLE MESSAGE: line 24: Refused to get unsafe header "set-cookie2"
    5 CONSOLE MESSAGE: line 23: Refused to get unsafe header "set-cookie"
    6 CONSOLE MESSAGE: line 24: Refused to get unsafe header "set-cookie2"
    71
    82PASS XMLHttpRequest: getResponseHeader() custom/non-existent headers and cookies
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt

    r203732 r203815  
    22PASS Fetch ../resources/top.txt with no-cors mode
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    4 FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     4PASS Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt

    r203732 r203815  
    22PASS Fetch ../resources/top.txt with no-cors mode
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    4 FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     4PASS Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt

    r203732 r203815  
    11
    2 FAIL Same domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     2PASS Same domain different port [no-cors mode]
    33PASS Same domain different port [server forbid CORS]
    4 FAIL Same domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    5 FAIL Same domain different protocol different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     4PASS Same domain different port [cors mode]
     5PASS Same domain different protocol different port [no-cors mode]
    66PASS Same domain different protocol different port [server forbid CORS]
    7 FAIL Same domain different protocol different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    8 FAIL Cross domain basic usage [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     7PASS Same domain different protocol different port [cors mode]
     8PASS Cross domain basic usage [no-cors mode]
    99PASS Cross domain basic usage [server forbid CORS]
    10 FAIL Cross domain basic usage [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    11 FAIL Cross domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     10PASS Cross domain basic usage [cors mode]
     11PASS Cross domain different port [no-cors mode]
    1212PASS Cross domain different port [server forbid CORS]
    13 FAIL Cross domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    14 FAIL Cross domain different protocol [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     13PASS Cross domain different port [cors mode]
     14PASS Cross domain different protocol [no-cors mode]
    1515PASS Cross domain different protocol [server forbid CORS]
    16 FAIL Cross domain different protocol [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
     16PASS Cross domain different protocol [cors mode]
    1717
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/cors/cors-filtering-expected.txt

    r203732 r203815  
    11
    2 FAIL CORS filter on Cache-Control header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    3 FAIL CORS filter on Content-Language header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    4 FAIL CORS filter on Content-Type header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    5 FAIL CORS filter on Expires header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    6 FAIL CORS filter on Last-Modified header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    7 FAIL CORS filter on Pragma header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    8 FAIL CORS filter on Age header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    9 FAIL CORS filter on Server header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    10 FAIL CORS filter on Warning header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    11 FAIL CORS filter on Content-Length header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    12 FAIL CORS filter on Set-Cookie header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    13 FAIL CORS filter on Set-Cookie2 header assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    14 FAIL CORS filter on Age header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    15 FAIL CORS filter on Server header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    16 FAIL CORS filter on Warning header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    17 FAIL CORS filter on Content-Length header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    18 FAIL CORS filter on Set-Cookie header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
    19 FAIL CORS filter on Set-Cookie2 header, header is exposed assert_equals: CORS fetch's response has cors type expected "cors" but got "basic"
     2PASS CORS filter on Cache-Control header
     3PASS CORS filter on Content-Language header
     4PASS CORS filter on Content-Type header
     5PASS CORS filter on Expires header
     6PASS CORS filter on Last-Modified header
     7PASS CORS filter on Pragma header
     8PASS CORS filter on Age header
     9PASS CORS filter on Server header
     10PASS CORS filter on Warning header
     11PASS CORS filter on Content-Length header
     12PASS CORS filter on Set-Cookie header
     13PASS CORS filter on Set-Cookie2 header
     14PASS CORS filter on Age header, header is exposed
     15PASS CORS filter on Server header, header is exposed
     16PASS CORS filter on Warning header, header is exposed
     17PASS CORS filter on Content-Length header, header is exposed
     18PASS CORS filter on Set-Cookie header, header is exposed
     19PASS CORS filter on Set-Cookie2 header, header is exposed
    2020
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-cache-expected.txt

    r203732 r203815  
    3838PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects with Etag and stale response
    3939PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects with date and stale response
    40 FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.5708867760543104\""
    41 FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Tue, 26 Jul 2016 18:03:51 GMT"
     40FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.5142751701087829\""
     41FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Wed, 27 Jul 2016 15:14:16 GMT"
    4242FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response assert_equals: expected 2 but got 1
    4343FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response assert_equals: expected 2 but got 1
    44 FAIL RequestCache "no-store" mode does not store the response in the cache with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.03236160265570209\""
    45 FAIL RequestCache "no-store" mode does not store the response in the cache with date and stale response assert_equals: expected (undefined) undefined but got (string) "Tue, 26 Jul 2016 18:03:51 GMT"
     44FAIL RequestCache "no-store" mode does not store the response in the cache with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.8439838765231941\""
     45FAIL RequestCache "no-store" mode does not store the response in the cache with date and stale response assert_equals: expected (undefined) undefined but got (string) "Wed, 27 Jul 2016 15:14:16 GMT"
    4646FAIL RequestCache "no-store" mode does not store the response in the cache with Etag and fresh response assert_equals: expected 2 but got 1
    4747FAIL RequestCache "no-store" mode does not store the response in the cache with date and fresh response assert_equals: expected 2 but got 1
     
    9090PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with Etag and fresh response
    9191PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with date and fresh response
    92 FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.32037580965802115\""
    93 FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Tue, 26 Jul 2016 18:03:51 GMT"
     92FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.7100561973865757\""
     93FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Wed, 27 Jul 2016 15:14:16 GMT"
    9494FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response assert_equals: expected 2 but got 1
    9595FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response assert_equals: expected 2 but got 1
     
    9898PASS RequestCache "reload" mode does store the response in the cache with Etag and fresh response
    9999PASS RequestCache "reload" mode does store the response in the cache with date and fresh response
    100 FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.702774010433134\""
    101 FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and stale response assert_equals: expected (undefined) undefined but got (string) "Tue, 26 Jul 2016 18:03:51 GMT"
     100FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.45824924441966697\""
     101FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and stale response assert_equals: expected (undefined) undefined but got (string) "Wed, 27 Jul 2016 15:14:16 GMT"
    102102FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and fresh response assert_equals: expected 2 but got 1
    103103FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and fresh response assert_equals: expected 2 but got 1
  • trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt

    r203732 r203815  
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    44FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode promise_test: Unhandled rejection with value: object "TypeError: Type error"
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt

    r203732 r203815  
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    44FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode promise_test: Unhandled rejection with value: object "TypeError: Type error"
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/platform/ios-simulator-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt

    r203732 r203815  
    11
    2 FAIL Same domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     2PASS Same domain different port [no-cors mode]
    33PASS Same domain different port [server forbid CORS]
    4 FAIL Same domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
     4PASS Same domain different port [cors mode]
    55FAIL Same domain different protocol different port [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    66PASS Same domain different protocol different port [server forbid CORS]
    77FAIL Same domain different protocol different port [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    8 FAIL Cross domain basic usage [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     8PASS Cross domain basic usage [no-cors mode]
    99PASS Cross domain basic usage [server forbid CORS]
    10 FAIL Cross domain basic usage [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    11 FAIL Cross domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     10PASS Cross domain basic usage [cors mode]
     11PASS Cross domain different port [no-cors mode]
    1212PASS Cross domain different port [server forbid CORS]
    13 FAIL Cross domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
     13PASS Cross domain different port [cors mode]
    1414FAIL Cross domain different protocol [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    1515PASS Cross domain different protocol [server forbid CORS]
  • trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-expected.txt

    r203732 r203815  
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    44FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode promise_test: Unhandled rejection with value: object "TypeError: Type error"
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/basic/mode-no-cors-worker-expected.txt

    r203732 r203815  
    33PASS Fetch http://localhost:8800/fetch/api/resources/top.txt with no-cors mode
    44FAIL Fetch https://localhost:9443/fetch/api/resources/top.txt with no-cors mode promise_test: Unhandled rejection with value: object "TypeError: Type error"
    5 FAIL Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode assert_equals: Opaque filter: status is 0 expected 0 but got 200
     5PASS Fetch http://localhost:8801/fetch/api/resources/top.txt with no-cors mode
    66
  • trunk/LayoutTests/platform/mac-wk2/imported/w3c/web-platform-tests/fetch/api/cors/cors-basic-expected.txt

    r203732 r203815  
    11
    2 FAIL Same domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     2PASS Same domain different port [no-cors mode]
    33PASS Same domain different port [server forbid CORS]
    4 FAIL Same domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
     4PASS Same domain different port [cors mode]
    55FAIL Same domain different protocol different port [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    66PASS Same domain different protocol different port [server forbid CORS]
    77FAIL Same domain different protocol different port [cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    8 FAIL Cross domain basic usage [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     8PASS Cross domain basic usage [no-cors mode]
    99PASS Cross domain basic usage [server forbid CORS]
    10 FAIL Cross domain basic usage [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
    11 FAIL Cross domain different port [no-cors mode] assert_equals: Opaque filter: status is 0 expected 0 but got 200
     10PASS Cross domain basic usage [cors mode]
     11PASS Cross domain different port [no-cors mode]
    1212PASS Cross domain different port [server forbid CORS]
    13 FAIL Cross domain different port [cors mode] assert_equals: CORS response's type is cors expected "cors" but got "basic"
     13PASS Cross domain different port [cors mode]
    1414FAIL Cross domain different protocol [no-cors mode] promise_test: Unhandled rejection with value: object "TypeError: Type error"
    1515PASS Cross domain different protocol [server forbid CORS]
  • trunk/Source/WebCore/ChangeLog

    r203814 r203815  
     12016-07-28  Youenn Fablet  <youenn@apple.com>
     2
     3        Compute fetch response type in case of cross-origin requests
     4        https://bugs.webkit.org/show_bug.cgi?id=158565
     5
     6        Reviewed by Alex Christensen.
     7
     8        Covered by rebased tests.
     9
     10        Implementing Response filtering based on Response tainting in ResourceResponse.
     11        Refactoring code in FetchHeaders and CrossOriginAccessControl.cpp accordingly.
     12
     13        Computing response tainting in SubresourceLoader for all resources.
     14        This is used by DocumentThreadableLoader which now filters responses accordingly for all its clients including fetch and XHR.
     15
     16        Response tainting notably allows computing the response type and filtering out headers in case of cross origin responses.
     17
     18        Removing the filtering implemented in XMLHttpRequest as this is done before it gets access to the headers.
     19        This is triggering some rebasing in the XHR tests as error messages triggered by trying to access unsafe headers no longer happen.
     20
     21        This filtering currently requires creating a new ResourceResponse object from the one sent from CachedResource.
     22        This is done so as the same ResourceResponse may be reused accross loads and may be filtered differently by given to two different DocumentThreadableLoader
     23        This can be mitigated in the future by changing ThreadableLoaderClient API to pass a ResourceResponse&&.
     24
     25        * Modules/fetch/FetchHeaders.cpp: Moving header checking in HTTParsers.h/.cpp
     26        (WebCore::isForbiddenHeaderName): Deleted.
     27        (WebCore::isForbiddenResponseHeaderName): Deleted.
     28        (WebCore::isSimpleHeader): Deleted.
     29        * loader/CrossOriginAccessControl.cpp:
     30        (WebCore::parseAccessControlExposeHeadersAllowList): Deleted.
     31        * loader/CrossOriginAccessControl.h: Moving header checking in HTTParsers.h/.cpp
     32        * loader/DocumentThreadableLoader.cpp:
     33        (WebCore::DocumentThreadableLoader::responseReceived):
     34        (WebCore::DocumentThreadableLoader::didReceiveResponse): Doing response filtering. Since underlying loaders are
     35        not yet aware that fetch mode may be cors (it is always no-cors currently), the tainting needs to be updated.
     36        (WebCore::DocumentThreadableLoader::loadRequest): Computing response tainting in case of synchronous calls to ensure headers are filtered for synchronous XHR.
     37        * loader/DocumentThreadableLoader.h:
     38        * loader/SubresourceLoader.cpp:
     39        (WebCore::SubresourceLoader::init): Getting origin from its resource and setting response tainting accordingly
     40        (WebCore::SubresourceLoader::willSendRequestInternal): Always calling checkRedirectionCrossOriginAccessControl
     41        to ensure response tainting is computed, even for no-cors resources.
     42        (WebCore::SubresourceLoader::didReceiveResponse):
     43        (WebCore::SubresourceLoader::checkRedirectionCrossOriginAccessControl): Computing response tainting in case of redirection.
     44        * loader/SubresourceLoader.h:
     45        * loader/cache/CachedResource.cpp:
     46        (WebCore::CachedResource::load): Computing resource origin from the HTTP headers or from the document if none is
     47        set in the HTTP headers.
     48        (WebCore::CachedResource::setCrossOrigin): Helper routine to set response tainting.
     49        (WebCore::CachedResource::isCrossOrigin): Helper routine to know whether resource is cross origin
     50        (WebCore::CachedResource::isClean):
     51        (WebCore::CachedResource::setResponse): Removing m_responseType
     52        * loader/cache/CachedResource.h:
     53        (WebCore::CachedResource::responseTainting):
     54        (WebCore::CachedResource::origin):
     55        (WebCore::CachedResource::setOpaqueRedirect): Deleted.
     56        * platform/network/HTTPParsers.cpp: Implementing safe response header checking
     57        (WebCore::parseAccessControlExposeHeadersAllowList):
     58        (WebCore::isForbiddenHeaderName):
     59        (WebCore::isForbiddenResponseHeaderName):
     60        (WebCore::isSimpleHeader):
     61        (WebCore::isCrossOriginSafeHeader):
     62        * platform/network/HTTPParsers.h:
     63        * platform/network/ResourceRequestBase.cpp:
     64        (WebCore::ResourceRequestBase::hasHTTPOrigin): Added.
     65        (WebCore::ResourceRequestBase::clearHTTPOrigin):
     66        * platform/network/ResourceRequestBase.h:
     67        * platform/network/ResourceResponseBase.cpp: Implementation of response filtering.
     68        (WebCore::ResourceResponseBase::filterResponse):
     69        * platform/network/ResourceResponseBase.h:
     70        * xml/XMLHttpRequest.cpp:
     71        (WebCore::isSetCookieHeader): Deleted.
     72        (WebCore::XMLHttpRequest::getAllResponseHeaders): Removing header filtering since DocumentThreadableLoader does it.
     73        (WebCore::XMLHttpRequest::getResponseHeader): Ditto.
     74
    1752016-07-27  Romain Bellessort  <romain.bellessort@crf.canon.fr>
    276
  • trunk/Source/WebCore/Modules/fetch/FetchHeaders.cpp

    r200546 r203815  
    3636
    3737namespace WebCore {
    38 
    39 // FIXME: Optimize these routines for HTTPHeaderMap keys and/or refactor them with XMLHttpRequest code.
    40 static bool isForbiddenHeaderName(const String& name)
    41 {
    42     HTTPHeaderName headerName;
    43     if (findHTTPHeaderName(name, headerName)) {
    44         switch (headerName) {
    45         case HTTPHeaderName::AcceptCharset:
    46         case HTTPHeaderName::AcceptEncoding:
    47         case HTTPHeaderName::AccessControlRequestHeaders:
    48         case HTTPHeaderName::AccessControlRequestMethod:
    49         case HTTPHeaderName::Connection:
    50         case HTTPHeaderName::ContentLength:
    51         case HTTPHeaderName::Cookie:
    52         case HTTPHeaderName::Cookie2:
    53         case HTTPHeaderName::Date:
    54         case HTTPHeaderName::DNT:
    55         case HTTPHeaderName::Expect:
    56         case HTTPHeaderName::Host:
    57         case HTTPHeaderName::KeepAlive:
    58         case HTTPHeaderName::Origin:
    59         case HTTPHeaderName::Referer:
    60         case HTTPHeaderName::TE:
    61         case HTTPHeaderName::Trailer:
    62         case HTTPHeaderName::TransferEncoding:
    63         case HTTPHeaderName::Upgrade:
    64         case HTTPHeaderName::Via:
    65             return true;
    66         default:
    67             break;
    68         }
    69     }
    70     return startsWithLettersIgnoringASCIICase(name, "sec-") || startsWithLettersIgnoringASCIICase(name, "proxy-");
    71 }
    72 
    73 static bool isForbiddenResponseHeaderName(const String& name)
    74 {
    75     return equalLettersIgnoringASCIICase(name, "set-cookie") || equalLettersIgnoringASCIICase(name, "set-cookie2");
    76 }
    77 
    78 static bool isSimpleHeader(const String& name, const String& value)
    79 {
    80     HTTPHeaderName headerName;
    81     if (!findHTTPHeaderName(name, headerName))
    82         return false;
    83     switch (headerName) {
    84     case HTTPHeaderName::Accept:
    85     case HTTPHeaderName::AcceptLanguage:
    86     case HTTPHeaderName::ContentLanguage:
    87         return true;
    88     case HTTPHeaderName::ContentType: {
    89         String mimeType = extractMIMETypeFromMediaType(value);
    90         return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");
    91     }
    92     default:
    93         return false;
    94     }
    95 }
    9638
    9739static bool canWriteHeader(const String& name, const String& value, FetchHeaders::Guard guard, ExceptionCode& ec)
  • trunk/Source/WebCore/loader/CrossOriginAccessControl.cpp

    r202674 r203815  
    181181}
    182182
    183 void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet& headerSet)
    184 {
    185     Vector<String> headers;
    186     headerValue.split(',', false, headers);
    187     for (auto& header : headers) {
    188         String strippedHeader = header.stripWhiteSpace();
    189         if (!strippedHeader.isEmpty())
    190             headerSet.add(strippedHeader);
    191     }
    192 }
    193 
    194183} // namespace WebCore
  • trunk/Source/WebCore/loader/CrossOriginAccessControl.h

    r202674 r203815  
    3030#include "ResourceHandleTypes.h"
    3131#include <wtf/Forward.h>
    32 #include <wtf/HashSet.h>
    33 #include <wtf/text/StringHash.h>
    3432
    3533namespace WebCore {
    36 
    37 typedef HashSet<String, ASCIICaseInsensitiveHash> HTTPHeaderSet;
    3834
    3935class HTTPHeaderMap;
     
    5652
    5753bool passesAccessControlCheck(const ResourceResponse&, StoredCredentials, SecurityOrigin&, String& errorDescription);
    58 void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet&);
    5954
    6055} // namespace WebCore
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp

    r203720 r203815  
    269269{
    270270    ASSERT_UNUSED(resource, resource == m_resource);
    271     didReceiveResponse(m_resource->identifier(), response);
    272 }
    273 
    274 void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
     271    didReceiveResponse(m_resource->identifier(), response, m_resource->responseTainting());
     272}
     273
     274void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response, ResourceResponse::Tainting tainting)
    275275{
    276276    ASSERT(m_client);
     
    284284    }
    285285
    286     m_client->didReceiveResponse(identifier, response);
     286    ASSERT(response.type() != ResourceResponse::Type::Error);
     287    if (response.type() == ResourceResponse::Type::Default) {
     288        // FIXME: To be removed once the real fetch mode is passed to underlying loaders.
     289        if (options().mode == FetchOptions::Mode::Cors && tainting == ResourceResponse::Tainting::Opaque)
     290            tainting = ResourceResponse::Tainting::Cors;
     291        m_client->didReceiveResponse(identifier, ResourceResponse::filterResponse(response, tainting));
     292    } else {
     293        ASSERT(response.isNull() && response.type() == ResourceResponse::Type::Opaqueredirect);
     294        m_client->didReceiveResponse(identifier, response);
     295    }
    287296}
    288297
     
    387396            // We don't want XMLHttpRequest to raise an exception for file:// resources, see <rdar://problem/4962298>.
    388397            // FIXME: XMLHttpRequest quirks should be in XMLHttpRequest code, not in DocumentThreadableLoader.cpp.
    389             didReceiveResponse(identifier, response);
     398            didReceiveResponse(identifier, response, ResourceResponse::Tainting::Basic);
    390399            didFinishLoading(identifier, 0.0);
    391400            return;
     
    410419    }
    411420
    412     didReceiveResponse(identifier, response);
     421    ResourceResponse::Tainting tainting = ResourceResponse::Tainting::Basic;
     422    if (!m_sameOriginRequest)
     423        tainting = m_options.mode == FetchOptions::Mode::Cors ? ResourceResponse::Tainting::Cors : ResourceResponse::Tainting::Opaque;
     424    didReceiveResponse(identifier, response, tainting);
    413425
    414426    if (data)
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.h

    r202674 r203815  
    3333
    3434#include "CrossOriginPreflightChecker.h"
     35#include "ResourceResponse.h"
    3536#include "SecurityOrigin.h"
    3637#include "ThreadableLoader.h"
     
    8283        void notifyFinished(CachedResource*) override;
    8384
    84         void didReceiveResponse(unsigned long identifier, const ResourceResponse&);
     85        void didReceiveResponse(unsigned long identifier, const ResourceResponse&, ResourceResponse::Tainting);
    8586        void didReceiveData(unsigned long identifier, const char* data, int dataLength);
    8687        void didFinishLoading(unsigned long identifier, double finishTime);
  • trunk/Source/WebCore/loader/SubresourceLoader.cpp

    r202811 r203815  
    77 *
    88 * 1.  Redistributions of source code must retain the above copyright
    9  *     notice, this list of conditions and the following disclaimer. 
     9 *     notice, this list of conditions and the following disclaimer.
    1010 * 2.  Redistributions in binary form must reproduce the above copyright
    1111 *     notice, this list of conditions and the following disclaimer in the
    12  *     documentation and/or other materials provided with the distribution. 
     12 *     documentation and/or other materials provided with the distribution.
    1313 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
    1414 *     its contributors may be used to endorse or promote products derived
    15  *     from this software without specific prior written permission. 
     15 *     from this software without specific prior written permission.
    1616 *
    1717 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     
    152152    // SubresourceLoader could use the document origin as a default and set PotentiallyCrossOriginEnabled requests accordingly.
    153153    // This would simplify resource loader users as they would only need to set fetch mode to Cors.
    154     if (options().mode == FetchOptions::Mode::Cors)
    155         m_origin = SecurityOrigin::createFromString(request.httpOrigin());
     154    m_origin = m_resource->origin();
     155    // https://fetch.spec.whatwg.org/#main-fetch, step 11, data URL here is considered not cross origin.
     156    if (!request.url().protocolIsData() && m_origin && !m_origin->canRequest(request.url()))
     157        m_resource->setCrossOrigin();
    156158
    157159    return true;
     
    181183                return;
    182184            }
    183             m_resource->setOpaqueRedirect();
    184             m_resource->responseReceived({ });
     185
     186            ResourceResponse opaqueRedirectedResponse;
     187            opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect);
     188            m_resource->responseReceived(opaqueRedirectedResponse);
    185189            didFinishLoading(currentTime());
    186190            return;
     
    203207        }
    204208
    205         if (options().mode == FetchOptions::Mode::Cors && !checkCrossOriginAccessControl(request(), redirectResponse, newRequest)) {
     209        if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest)) {
    206210            cancel();
    207211            return;
     
    296300        m_resource->finishLoading(buffer->copy().ptr());
    297301        clearResourceData();
    298         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.       
    299         // After the first multipart section is complete, signal to delegates that this load is "finished" 
     302        // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
     303        // After the first multipart section is complete, signal to delegates that this load is "finished"
    300304        m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
    301305        didFinishLoadingOnePart(0);
     
    397401}
    398402
    399 bool SubresourceLoader::checkCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest)
    400 {
    401     if (m_origin->canRequest(newRequest.url()))
     403bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest)
     404{
     405    ASSERT(options().mode != FetchOptions::Mode::SameOrigin);
     406
     407    bool shouldCheckCrossOrigin = options().mode == FetchOptions::Mode::Cors && m_resource->isCrossOrigin();
     408
     409    if (!(m_origin && m_origin->canRequest(newRequest.url())))
     410        m_resource->setCrossOrigin();
     411
     412    if (!shouldCheckCrossOrigin)
    402413        return true;
    403414
     415    ASSERT(m_origin);
    404416    String errorDescription;
    405417    bool responsePassesCORS = m_origin->canRequest(previousRequest.url())
  • trunk/Source/WebCore/loader/SubresourceLoader.h

    r201761 r203815  
    77 *
    88 * 1.  Redistributions of source code must retain the above copyright
    9  *     notice, this list of conditions and the following disclaimer. 
     9 *     notice, this list of conditions and the following disclaimer.
    1010 * 2.  Redistributions in binary form must reproduce the above copyright
    1111 *     notice, this list of conditions and the following disclaimer in the
    12  *     documentation and/or other materials provided with the distribution. 
     12 *     documentation and/or other materials provided with the distribution.
    1313 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
    1414 *     its contributors may be used to endorse or promote products derived
    15  *     from this software without specific prior written permission. 
     15 *     from this software without specific prior written permission.
    1616 *
    1717 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     
    9292
    9393    bool checkForHTTPStatusCodeError();
    94     bool checkCrossOriginAccessControl(const ResourceRequest&, const ResourceResponse&, ResourceRequest& newRequest);
     94    bool checkRedirectionCrossOriginAccessControl(const ResourceRequest&, const ResourceResponse&, ResourceRequest& newRequest);
    9595
    9696    void didReceiveDataOrBuffer(const char*, int, RefPtr<SharedBuffer>&&, long long encodedDataLength, DataPayloadType);
  • trunk/Source/WebCore/loader/cache/CachedResource.cpp

    r202985 r203815  
    282282    m_resourceRequest.setPriority(loadPriority());
    283283
    284     addAdditionalRequestHeaders(cachedResourceLoader);
     284    if (type() != MainResource) {
     285        if (m_resourceRequest.hasHTTPOrigin())
     286            m_origin = SecurityOrigin::createFromString(m_resourceRequest.httpOrigin());
     287        else
     288            m_origin = cachedResourceLoader.document()->securityOrigin();
     289        ASSERT(m_origin);
     290        addAdditionalRequestHeaders(cachedResourceLoader);
     291    }
    285292
    286293    // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers.
     
    366373        return true;
    367374    return passesAccessControlCheck(securityOrigin);
     375}
     376
     377void CachedResource::setCrossOrigin()
     378{
     379    ASSERT(m_options.mode != FetchOptions::Mode::SameOrigin);
     380    m_responseTainting = (m_options.mode == FetchOptions::Mode::Cors) ? ResourceResponse::Tainting::Cors : ResourceResponse::Tainting::Opaque;
     381}
     382
     383bool CachedResource::isCrossOrigin() const
     384{
     385    return m_responseTainting != ResourceResponse::Tainting::Basic;
     386}
     387
     388bool CachedResource::isClean() const
     389{
     390    // https://html.spec.whatwg.org/multipage/infrastructure.html#cors-same-origin
     391    return !loadFailedOrCanceled() && m_responseTainting != ResourceResponse::Tainting::Opaque;
    368392}
    369393
     
    427451void CachedResource::setResponse(const ResourceResponse& response)
    428452{
     453    ASSERT(m_response.type() == ResourceResponse::Type::Default);
    429454    m_response = response;
    430     m_response.setType(m_responseType);
    431455    m_response.setRedirected(m_redirectChainCacheStatus.status != RedirectChainCacheStatus::NoRedirection);
    432456
  • trunk/Source/WebCore/loader/cache/CachedResource.h

    r201805 r203815  
    201201    virtual bool shouldCacheResponse(const ResourceResponse&) { return true; }
    202202    void setResponse(const ResourceResponse&);
    203     void setOpaqueRedirect() { m_responseType = ResourceResponseBase::Type::Opaqueredirect; }
    204203    const ResourceResponse& response() const { return m_response; }
    205204    // This is the same as response() except after HTTP redirect to data: URL.
    206205    const ResourceResponse& responseForSameOriginPolicyChecks() const;
     206
     207    void setCrossOrigin();
     208    bool isCrossOrigin() const;
     209    bool isClean() const;
     210    ResourceResponse::Tainting responseTainting() const { return m_responseTainting; }
     211
     212    SecurityOrigin* origin() const { return m_origin.get(); }
    207213
    208214    bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; }
     
    225231
    226232    bool allowsCaching() const { return m_options.cachingPolicy() == CachingPolicy::AllowCaching; }
    227    
     233
    228234    virtual void destroyDecodedData() { }
    229235
     
    285291    ResourceLoaderOptions m_options;
    286292    ResourceResponse m_response;
    287     ResourceResponseBase::Type m_responseType { ResourceResponseBase::Type::Basic };
     293    ResourceResponse::Tainting m_responseTainting { ResourceResponse::Tainting::Basic };
    288294    ResourceResponse m_redirectResponseForSameOriginPolicyChecks;
    289295    RefPtr<SharedBuffer> m_data;
     
    314320
    315321    ResourceError m_error;
     322    RefPtr<SecurityOrigin> m_origin;
    316323
    317324    double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache
  • trunk/Source/WebCore/platform/network/HTTPParsers.cpp

    r202910 r203815  
    3434#include "HTTPParsers.h"
    3535
     36#include "HTTPHeaderNames.h"
    3637#include <wtf/DateMath.h>
    3738#include <wtf/NeverDestroyed.h>
     
    756757}
    757758
    758 }
     759void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet& headerSet)
     760{
     761    Vector<String> headers;
     762    headerValue.split(',', false, headers);
     763    for (auto& header : headers) {
     764        String strippedHeader = header.stripWhiteSpace();
     765        if (!strippedHeader.isEmpty())
     766            headerSet.add(strippedHeader);
     767    }
     768}
     769
     770// Implememtnation of https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
     771bool isForbiddenHeaderName(const String& name)
     772{
     773    HTTPHeaderName headerName;
     774    if (findHTTPHeaderName(name, headerName)) {
     775        switch (headerName) {
     776        case HTTPHeaderName::AcceptCharset:
     777        case HTTPHeaderName::AcceptEncoding:
     778        case HTTPHeaderName::AccessControlRequestHeaders:
     779        case HTTPHeaderName::AccessControlRequestMethod:
     780        case HTTPHeaderName::Connection:
     781        case HTTPHeaderName::ContentLength:
     782        case HTTPHeaderName::Cookie:
     783        case HTTPHeaderName::Cookie2:
     784        case HTTPHeaderName::Date:
     785        case HTTPHeaderName::DNT:
     786        case HTTPHeaderName::Expect:
     787        case HTTPHeaderName::Host:
     788        case HTTPHeaderName::KeepAlive:
     789        case HTTPHeaderName::Origin:
     790        case HTTPHeaderName::Referer:
     791        case HTTPHeaderName::TE:
     792        case HTTPHeaderName::Trailer:
     793        case HTTPHeaderName::TransferEncoding:
     794        case HTTPHeaderName::Upgrade:
     795        case HTTPHeaderName::Via:
     796            return true;
     797        default:
     798            break;
     799        }
     800    }
     801    return startsWithLettersIgnoringASCIICase(name, "sec-") || startsWithLettersIgnoringASCIICase(name, "proxy-");
     802}
     803
     804bool isForbiddenResponseHeaderName(const String& name)
     805{
     806    return equalLettersIgnoringASCIICase(name, "set-cookie") || equalLettersIgnoringASCIICase(name, "set-cookie2");
     807}
     808
     809bool isSimpleHeader(const String& name, const String& value)
     810{
     811    HTTPHeaderName headerName;
     812    if (!findHTTPHeaderName(name, headerName))
     813        return false;
     814    switch (headerName) {
     815    case HTTPHeaderName::Accept:
     816    case HTTPHeaderName::AcceptLanguage:
     817    case HTTPHeaderName::ContentLanguage:
     818        return true;
     819    case HTTPHeaderName::ContentType: {
     820        String mimeType = extractMIMETypeFromMediaType(value);
     821        return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");
     822    }
     823    default:
     824        return false;
     825    }
     826}
     827
     828bool isCrossOriginSafeHeader(HTTPHeaderName name, const HTTPHeaderSet& accessControlExposeHeaderSet)
     829{
     830    switch (name) {
     831    case HTTPHeaderName::CacheControl:
     832    case HTTPHeaderName::ContentLanguage:
     833    case HTTPHeaderName::ContentType:
     834    case HTTPHeaderName::Expires:
     835    case HTTPHeaderName::LastModified:
     836    case HTTPHeaderName::Pragma:
     837    case HTTPHeaderName::Accept:
     838        return true;
     839    case HTTPHeaderName::SetCookie:
     840    case HTTPHeaderName::SetCookie2:
     841        return false;
     842    default:
     843        break;
     844    }
     845    return accessControlExposeHeaderSet.contains(httpHeaderNameString(name).toStringWithoutCopying());
     846}
     847
     848bool isCrossOriginSafeHeader(const String& name, const HTTPHeaderSet& accessControlExposeHeaderSet)
     849{
     850#ifndef ASSERT_DISABLED
     851    HTTPHeaderName headerName;
     852    ASSERT(!findHTTPHeaderName(name, headerName));
     853#endif
     854    return accessControlExposeHeaderSet.contains(name);
     855}
     856
     857}
  • trunk/Source/WebCore/platform/network/HTTPParsers.h

    r202910 r203815  
    3333
    3434#include <wtf/Forward.h>
     35#include <wtf/HashSet.h>
    3536#include <wtf/Optional.h>
    3637#include <wtf/Vector.h>
     38#include <wtf/text/StringHash.h>
    3739#include <wtf/text/WTFString.h>
    3840
    3941namespace WebCore {
     42
     43typedef HashSet<String, ASCIICaseInsensitiveHash> HTTPHeaderSet;
     44
     45enum class HTTPHeaderName;
    4046
    4147enum class XSSProtectionDisposition {
     
    96102size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned char>& body);
    97103
     104void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet&);
     105
     106// HTTP Header routine as per https://fetch.spec.whatwg.org/#terminology-headers
     107bool isForbiddenHeaderName(const String&);
     108bool isForbiddenResponseHeaderName(const String&);
     109bool isSimpleHeader(const String& name, const String& value);
     110bool isCrossOriginSafeHeader(HTTPHeaderName, const HTTPHeaderSet&);
     111bool isCrossOriginSafeHeader(const String&, const HTTPHeaderSet&);
     112
    98113inline bool isHTTPSpace(UChar character)
    99114{
  • trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp

    r201623 r203815  
    314314}
    315315
     316bool ResourceRequestBase::hasHTTPOrigin() const
     317{
     318    return m_httpHeaderFields.contains(HTTPHeaderName::Origin);
     319}
     320
    316321void ResourceRequestBase::clearHTTPOrigin()
    317322{
    318     updateResourceRequest(); 
     323    updateResourceRequest();
    319324
    320325    m_httpHeaderFields.remove(HTTPHeaderName::Origin);
  • trunk/Source/WebCore/platform/network/ResourceRequestBase.h

    r201623 r203815  
    2323 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    2424 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2626 */
    2727
     
    101101    WEBCORE_EXPORT void setHTTPReferrer(const String&);
    102102    WEBCORE_EXPORT void clearHTTPReferrer();
    103    
     103
    104104    String httpOrigin() const;
     105    bool hasHTTPOrigin() const;
    105106    void setHTTPOrigin(const String&);
    106107    WEBCORE_EXPORT void clearHTTPOrigin();
  • trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp

    r203153 r203815  
    100100}
    101101
     102ResourceResponse ResourceResponseBase::filterResponse(const ResourceResponse& response, ResourceResponse::Tainting tainting)
     103{
     104    if (tainting == ResourceResponse::Tainting::Opaque) {
     105        ResourceResponse opaqueResponse;
     106        opaqueResponse.setType(ResourceResponse::Type::Opaque);
     107        return opaqueResponse;
     108    }
     109
     110    ResourceResponse filteredResponse = response;
     111    // Let's initialize filteredResponse to remove some header fields.
     112    filteredResponse.lazyInit(AllFields);
     113
     114    if (tainting == ResourceResponse::Tainting::Basic) {
     115        filteredResponse.setType(ResourceResponse::Type::Basic);
     116        filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie);
     117        filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie2);
     118        return filteredResponse;
     119    }
     120
     121    ASSERT(tainting == ResourceResponse::Tainting::Cors);
     122    filteredResponse.setType(ResourceResponse::Type::Cors);
     123
     124    HTTPHeaderSet accessControlExposeHeaderSet;
     125    parseAccessControlExposeHeadersAllowList(response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
     126    filteredResponse.m_httpHeaderFields.uncommonHeaders().removeIf([&](auto& entry) {
     127        return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet);
     128    });
     129    filteredResponse.m_httpHeaderFields.commonHeaders().removeIf([&](auto& entry) {
     130        return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet);
     131    });
     132
     133    return filteredResponse;
     134}
     135
    102136// FIXME: Name does not make it clear this is true for HTTPS!
    103137bool ResourceResponseBase::isHTTP() const
     
    112146    lazyInit(CommonFieldsOnly);
    113147
    114     return m_url; 
     148    return m_url;
    115149}
    116150
  • trunk/Source/WebCore/platform/network/ResourceResponseBase.h

    r203153 r203815  
    6666    static ResourceResponse fromCrossThreadData(CrossThreadData&&);
    6767
     68    enum class Tainting { Basic, Cors, Opaque };
     69    static ResourceResponse filterResponse(const ResourceResponse&, Tainting);
     70
    6871    bool isNull() const { return m_isNull; }
    6972    WEBCORE_EXPORT bool isHTTP() const;
  • trunk/Source/WebCore/xml/XMLHttpRequest.cpp

    r203494 r203815  
    7676};
    7777
    78 static bool isSetCookieHeader(const String& name)
    79 {
    80     return equalLettersIgnoringASCIICase(name, "set-cookie") || equalLettersIgnoringASCIICase(name, "set-cookie2");
    81 }
    82 
    8378static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
    8479{
     
    907902    StringBuilder stringBuilder;
    908903
    909     HTTPHeaderSet accessControlExposeHeaderSet;
    910     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
    911    
    912904    for (const auto& header : m_response.httpHeaderFields()) {
    913         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
    914         //     1) If the client did have access to the fields, then it could read HTTP-only
    915         //        cookies; those cookies are supposed to be hidden from scripts.
    916         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
    917         //        know any widely used technique that requires access to them.
    918         //     3) Firefox has implemented this policy.
    919         if (isSetCookieHeader(header.key) && !securityOrigin()->canLoadLocalResources())
    920             continue;
    921 
    922         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(header.key) && !accessControlExposeHeaderSet.contains(header.key))
    923             continue;
    924 
    925905        stringBuilder.append(header.key);
    926906        stringBuilder.append(':');
     
    939919        return String();
    940920
    941     // See comment in getAllResponseHeaders above.
    942     if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
    943         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
    944         return String();
    945     }
    946 
    947     HTTPHeaderSet accessControlExposeHeaderSet;
    948     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
    949 
    950     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
    951         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
    952         return String();
    953     }
    954921    return m_response.httpHeaderField(name);
    955922}
Note: See TracChangeset for help on using the changeset viewer.