-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DIP] Add support for isolate-and-credentialless
This CL ensures that no-CORS cross-origin subresources requests from documents with Document-Isolation-Policy: isolate-and-credentialless are stripped of credentials. Since CORP checks already support Document-Isolation-Policy: isolate-and-credentialless, this is enough to enable cross-origin isolation in documents with Document-Isolation-Policy: isolate-and-credentialless. Bug: 349792240 Change-Id: I21bacba16a62dc8dd7c101450819466208815f42 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5867191 Commit-Queue: Camille Lamy <[email protected]> Reviewed-by: Kenichi Ishibashi <[email protected]> Reviewed-by: Alex Moshchuk <[email protected]> Cr-Commit-Position: refs/heads/main@{#1365616}
- Loading branch information
1 parent
ec97938
commit 5eec73e
Showing
17 changed files
with
1,452 additions
and
0 deletions.
There are no files selected for viewing
150 changes: 150 additions & 0 deletions
150
html/document-isolation-policy/credentialless-cache-storage.https.tentative.window.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// META: timeout=long | ||
// META: variant=?document | ||
// META: variant=?dedicated_worker | ||
// META: variant=?shared_worker | ||
// META: variant=?service_worker | ||
// META: script=/common/get-host-info.sub.js | ||
// META: script=/common/utils.js | ||
// META: script=/common/dispatcher/dispatcher.js | ||
// META: script=./resources/common.js | ||
|
||
// Fetch a resource and store it into CacheStorage from |storer| context. Then | ||
// check if it can be retrieved via CacheStorage.match from |retriever| context. | ||
const cacheStorageTest = ( | ||
description, | ||
storer, | ||
retriever, | ||
resource_headers, | ||
request_credential_mode, | ||
expectation | ||
) => { | ||
promise_test_parallel(async test => { | ||
const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN; | ||
const url = cross_origin + "/common/square.png?pipe=" + resource_headers + | ||
`&${token()}`; | ||
const this_token = token(); | ||
|
||
// Fetch a request from |stored|. Store the opaque response into | ||
// CacheStorage. | ||
send(storer, ` | ||
const cache = await caches.open("v1"); | ||
const fetch_request = new Request("${url}", { | ||
mode: 'no-cors', | ||
credentials: '${request_credential_mode}' | ||
}); | ||
const fetch_response = await fetch(fetch_request); | ||
await cache.put(fetch_request, fetch_response); | ||
send("${this_token}", "stored"); | ||
`); | ||
assert_equals(await receive(this_token), "stored"); | ||
|
||
// Retrieved it from |retriever|. | ||
send(retriever, ` | ||
const cache = await caches.open("v1"); | ||
try { | ||
const response = await cache.match("${url}"); | ||
send("${this_token}", "retrieved"); | ||
} catch (error) { | ||
send("${this_token}", "error"); | ||
} | ||
`); | ||
assert_equals(await receive(this_token), expectation); | ||
}, description); | ||
}; | ||
|
||
// Execute the same set of tests for every type of execution contexts: | ||
// Documents, DedicatedWorkers, SharedWorkers, and ServiceWorkers. The results | ||
// should be independent of the context. | ||
const environment = location.search.substr(1); | ||
const constructor = environments[environment]; | ||
|
||
const context_none = constructor(coep_none)[0]; | ||
const context_credentialless = constructor(dip_credentialless)[0]; | ||
const context_require_corp = constructor(dip_require_corp)[0]; | ||
|
||
cacheStorageTest(`[${environment}] none => none`, | ||
context_none, | ||
context_none, | ||
"", | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-credentialless`, | ||
context_none, | ||
context_credentialless, | ||
"", | ||
"include", | ||
"error"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-credentialless (omit)`, | ||
context_none, | ||
context_credentialless, | ||
"", | ||
"omit", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-credentialless + CORP`, | ||
context_none, | ||
context_credentialless, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-require-corp`, | ||
context_none, | ||
context_require_corp, | ||
"", | ||
"include", | ||
"error"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-require-corp (omit)`, | ||
context_none, | ||
context_require_corp, | ||
"", | ||
"include", | ||
"error"); | ||
cacheStorageTest(`[${environment}] none => isolate-and-require-corp + CORP`, | ||
context_none, | ||
context_require_corp, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); | ||
|
||
cacheStorageTest(`[${environment}] isolate-and-credentialless => none`, | ||
context_credentialless, | ||
context_none, | ||
"", | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] isolate-and-credentialless => isolate-and-credentialless`, | ||
context_credentialless, | ||
context_credentialless, | ||
"", | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] isolate-and-credentialless => isolate-and-require-corp`, | ||
context_credentialless, | ||
context_require_corp, | ||
"", | ||
"include", | ||
"error"); | ||
cacheStorageTest(`[${environment}] isolate-and-credentialless => isolate-and-require-corp + CORP`, | ||
context_credentialless, | ||
context_require_corp, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); | ||
|
||
cacheStorageTest(`[${environment}] isolate-and-require-corp => none`, | ||
context_require_corp, | ||
context_none, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] isolate-and-require-corp => isolate-and-credentialless`, | ||
context_require_corp, | ||
context_credentialless, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); | ||
cacheStorageTest(`[${environment}] isolate-and-require-corp => isolate-and-require-corp`, | ||
context_require_corp, | ||
context_require_corp, | ||
corp_cross_origin, | ||
"include", | ||
"retrieved"); |
85 changes: 85 additions & 0 deletions
85
html/document-isolation-policy/credentialless-cache.tentative.window.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// META: script=/common/get-host-info.sub.js | ||
// META: script=/common/utils.js | ||
// META: script=/common/dispatcher/dispatcher.js | ||
// META: script=./resources/common.js | ||
|
||
// With DIP:isolate-and-credentialless, requesting a resource without | ||
// credentials MUST NOT return a response requested with credentials. This would | ||
// be a security issue, since DIP:isolate-and-credentialless can be used to | ||
// enable crossOriginIsolation. | ||
// | ||
// The test the behavior of the HTTP cache: | ||
// 1. b.com stores cookie. | ||
// 2. a.com(DIP:none): request b.com's resource. | ||
// 3. a.com(DIP:isolate-and-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 = "dip_cache_key"; | ||
const cookie_value = "dip_cache_value"; | ||
const same_origin = get_host_info().HTTPS_ORIGIN; | ||
const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN; | ||
|
||
const GetCookie = (response) => { | ||
return parseCookies(JSON.parse(response))[cookie_key]; | ||
} | ||
|
||
// "same_origin" document with DIP:none. | ||
const w_dip_none_token = token(); | ||
const w_dip_none_url = same_origin + executor_path + dip_none + | ||
`&uuid=${w_dip_none_token}` | ||
const w_dip_none = window.open(w_dip_none_url); | ||
add_completion_callback(() => w_dip_none.close()); | ||
|
||
// "same_origin" document with DIP:isolate-and-credentialles. | ||
const w_dip_credentialless_token = token(); | ||
const w_dip_credentialless_url = same_origin + executor_path + | ||
dip_credentialless + `&uuid=${w_dip_credentialless_token}` | ||
const w_dip_credentialless = window.open(w_dip_credentialless_url); | ||
add_completion_callback(() => w_dip_credentialless.close()); | ||
|
||
const this_token = token(); | ||
|
||
// A request toward a "cross-origin" cacheable response. | ||
const request_token = token(); | ||
const request_url = cacheableShowRequestHeaders(cross_origin, request_token); | ||
|
||
promise_setup(async test => { | ||
await setCookie(cross_origin, cookie_key, cookie_value + cookie_same_site_none); | ||
}, "Set cookie"); | ||
|
||
// The "same-origin" DIP:none document fetches a "cross-origin" | ||
// resource. The request is sent with credentials. | ||
promise_setup(async test => { | ||
send(w_dip_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" DIP:isolate-andcredentialless 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_dip_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"); |
42 changes: 42 additions & 0 deletions
42
html/document-isolation-policy/credentialless-cross-origin-isolated.tentative.window.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// META: script=/common/get-host-info.sub.js | ||
// META: script=/common/utils.js | ||
// META: script=/common/dispatcher/dispatcher.js | ||
// META: script=./resources/common.js | ||
|
||
const http = get_host_info().HTTP_ORIGIN; | ||
const https = get_host_info().HTTPS_ORIGIN; | ||
|
||
let crossOriginIsolatedTest = ( | ||
description, | ||
origin , | ||
headers, | ||
expect_crossOriginIsolated) => { | ||
promise_test_parallel(async test => { | ||
const w_token = token(); | ||
const w_url = origin + executor_path + headers + `&uuid=${w_token}`; | ||
const w = window.open(w_url) | ||
add_completion_callback(() => w.close()); | ||
|
||
const this_token = token(); | ||
send(w_token, ` | ||
if (window.crossOriginIsolated) | ||
send("${this_token}", "crossOriginIsolated"); | ||
else | ||
send("${this_token}", "not isolated") | ||
`); | ||
assert_equals(await receive(this_token), expect_crossOriginIsolated); | ||
}, description); | ||
} | ||
|
||
crossOriginIsolatedTest("Main crossOriginIsolated case:", | ||
https, dip_credentialless, "crossOriginIsolated"); | ||
|
||
crossOriginIsolatedTest("Missing HTTPS:", | ||
http, dip_credentialless, "not isolated"); | ||
|
||
crossOriginIsolatedTest("Report-only:", | ||
https, dip_report_only_credentialless, "not isolated"); | ||
|
||
crossOriginIsolatedTest("Report-only + enforced:", | ||
https, dip_credentialless + | ||
dip_report_only_credentialless, "crossOriginIsolated"); |
Oops, something went wrong.