Skip to content

Commit

Permalink
[Credentialless]: Add tests about the HTTP cache.
Browse files Browse the repository at this point in the history
The request's includeCredentials isn't part of the HTTP cache key.

It means if:
- a.com requests c.com with credentials,
- b.com requests c.com without credentials
Then both a.com and b.com will get a response requested with credentials.

This seems problematic in general. The request's credential mode is not
respected, and a.com influences directly b.com. The partitioned HTTP
cache will solve one of the two problem.

With COEP:credentialless, we obviously don't want to request a resource
without credentials and get a response with credentials. That would be a
security issue.

Here is a WPT test about it.

Bug: whatwg/fetch#1253
Bug: 1218023
Change-Id: I888dc020a8ae770816d0fbc42e8803df3ba66392
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2961290
Reviewed-by: Mike West <[email protected]>
Commit-Queue: Arthur Sonzogni <[email protected]>
Cr-Commit-Position: refs/heads/master@{#893398}
  • Loading branch information
ArthurSonzogni authored and chromium-wpt-export-bot committed Jun 17, 2021
1 parent 03c5072 commit c42d9bc
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="./resources/common.js"></script>
<script src="./resources/dispatcher.js"></script>
<script>

// With COEP:credentialless, requesting a resource without credentials MUST NOT
// return a response requested with credentials. This would be a security
// issue, since COEP:credentialless can be used to enable crossOriginIsolation.
//
// The test the behavior of the HTTP cache:
// 1. b.com stores cookie.
// 2. a.com(COEP:unsafe-none): request b.com's resource.
// 3. a.com(COEP:credentialless): request b.com's resource.
//
// The first time, the resource is requested with credentials. The response is
// served with Cache-Control: max-age=31536000. It enters the cache.
// The second time, the resource is requested without credentials. The response
// in the cache must not be returned.

const cookie_key = "coep_cache_key";
const cookie_value = "coep_cache_value";
const same_origin = get_host_info().HTTPS_ORIGIN;
const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
const cacheable_response_path =
"/html/cross-origin-embedder-policy/credentialless/resources/cacheable-response.py";

const GetCookie = (response) => {
return parseCookies(JSON.parse(response))[cookie_key];
}

// "same_origin" document with COEP:unsafe-none.
const w_coep_none_token = token();
const w_coep_none_url = same_origin + executor_path + coep_none +
`&uuid=${w_coep_none_token}`
const w_coep_none = window.open(w_coep_none_url);
add_completion_callback(() => w_coep_none.close());

// "same_origin" document with COEP:credentialles.
const w_coep_credentialless_token = token();
const w_coep_credentialless_url = same_origin + executor_path +
coep_credentialless + `&uuid=${w_coep_credentialless_token}`
const w_coep_credentialless = window.open(w_coep_credentialless_url);
add_completion_callback(() => w_coep_credentialless.close());

const this_token = token();

// A request toward a "cross-origin" cacheable response.
const request_token = token();
const request_url = cross_origin + cacheable_response_path +
`?uuid=${request_token}`;

promise_setup(async test => {
await setCookie(cross_origin, cookie_key, cookie_value);
}, "Set cookie");

// The "same-origin" COEP:unsafe-none document fetchs a "cross-origin"
// resource. The request is sent with credentials.
promise_setup(async test => {
send(w_coep_none_token, `
await fetch("${request_url}", {
mode : "no-cors",
credentials: "include",
});
send("${this_token}", "Resource fetched");
`);

assert_equals(await receive(this_token), "Resource fetched");
assert_equals(await receive(request_token).then(GetCookie), cookie_value);
}, "Cache a response requested with credentials");

// The "same-origin" COEP:credentialless document fetches the same resource
// without credentials. The HTTP cache must not be used. Instead a second
// request must be made without credentials.
promise_test(async test => {
send(w_coep_credentialless_token, `
await fetch("${request_url}", {
mode : "no-cors",
credentials: "include",
});
send("${this_token}", "Resource fetched");
`);

assert_equals(await receive(this_token), "Resource fetched");

test.step_timeout(test.unreached_func("The HTTP cache has been used"), 1500);
assert_equals(await receive(request_token).then(GetCookie), undefined);
}, "The HTTP cache must not be used");

</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import json
from wptserve.utils import isomorphic_decode

# A server providing a cacheable response and storing the request's headers
# toward the `uuid` attribute stash.
def main(request, response):
# The response served is cacheable by the navigator:
response.headers.set(b"Cache-Control", b"max-age=31536000");

uuid = request.GET[b'uuid'];
headers = {};
for key, value in request.headers.items():
headers[isomorphic_decode(key)] = isomorphic_decode(request.headers[key])
headers = json.dumps(headers);

# The stash is accessed concurrently by many clients. A lock is used to
# avoid unterleaved read/write from different clients.
stash = request.server.stash;
with stash.lock:
queue = stash.take(uuid, '/coep-credentialless') or [];
queue.append(headers);
stash.put(uuid, queue, '/coep-credentialless');
return b"done";

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def main(request, response):
# The stash is accessed concurrently by many clients. A lock is used to
# avoid unterleaved read/write from different clients.
with stash.lock:
queue = stash.take(uuid) or [];
queue = stash.take(uuid, '/coep-credentialless') or [];

# Push into the |uuid| queue, the requested headers.
if b"show-headers" in request.GET:
Expand All @@ -45,5 +45,5 @@ def main(request, response):
else:
ret = queue.pop(0)

stash.put(uuid, queue)
stash.put(uuid, queue, '/coep-credentialless')
return ret;

0 comments on commit c42d9bc

Please sign in to comment.