-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fetch: Plumb request initiator through passthrough service workers.
This CL contains essentially two changes: 1. The request initiator origin is plumbed through service workers that do `fetch(evt.request)`. In addition to plumbing, this requires changes to how we validate navigation requests in the CorsURLLoaderFactory. 2. Tracks the original destination of a request passed through a service worker. This is then used in the network service to force SameSite=Lax cookies to treat the request as a main frame navigation where appropriate. For more detailed information about these changes please see the internal design doc at: https://docs.google.com/document/d/1KZscujuV7bCFEnzJW-0DaCPU-I40RJimQKoCcI0umTQ/edit?usp=sharing In addition, there is some discussion of these features in the following spec issues: whatwg/fetch#1321 whatwg/fetch#1327 The test includes WPT tests that verify navigation headers and SameSite cookies. Note, chrome has a couple expected failures in the SameSite cookie tests because of the "lax-allowing-unsafe" intervention that is currently enabled. See: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/TestExpectations;l=4635;drc=e8133cbf2469adb99c6610483ab78bcfb8cc4c76 Bug: 1115847,1241188 Change-Id: I7e236fa20aeabb705aef40fcf8d5c36da6d2798c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3115917 Reviewed-by: Matt Menke <[email protected]> Reviewed-by: Yutaka Hirano <[email protected]> Reviewed-by: Nasko Oskov <[email protected]> Reviewed-by: Łukasz Anforowicz <[email protected]> Commit-Queue: Ben Kelly <[email protected]> Cr-Commit-Position: refs/heads/main@{#936029}
- Loading branch information
1 parent
a965969
commit cd0951f
Showing
9 changed files
with
854 additions
and
1 deletion.
There are no files selected for viewing
558 changes: 558 additions & 0 deletions
558
service-workers/service-worker/navigation-headers.https.html
Large diffs are not rendered by default.
Oops, something went wrong.
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
2 changes: 2 additions & 0 deletions
2
service-workers/service-worker/resources/fetch-rewrite-worker.js.headers
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,2 @@ | ||
Content-Type: text/javascript | ||
Service-Worker-Allowed: / |
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,12 @@ | ||
<!DOCTYPE html> | ||
<meta name="referrer" content="origin"> | ||
<form method="POST" id="form"></form> | ||
<script> | ||
function onLoad() { | ||
const params = new URLSearchParams(self.location.search); | ||
const form = document.getElementById('form'); | ||
form.action = decodeURIComponent(params.get('target')); | ||
form.submit(); | ||
} | ||
self.addEventListener('load', onLoad); | ||
</script> |
10 changes: 10 additions & 0 deletions
10
service-workers/service-worker/resources/location-setter.html
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,10 @@ | ||
<!DOCTYPE html> | ||
<meta name="referrer" content="origin"> | ||
<script> | ||
function onLoad() { | ||
const params = new URLSearchParams(self.location.search); | ||
const target = decodeURIComponent(params.get('target')); | ||
self.location = target; | ||
} | ||
self.addEventListener('load', onLoad); | ||
</script> |
19 changes: 19 additions & 0 deletions
19
service-workers/service-worker/resources/navigation-headers-server.py
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,19 @@ | ||
def main(request, response): | ||
response.status = (200, b"OK") | ||
response.headers.set(b"Content-Type", b"text/html") | ||
return b""" | ||
<script> | ||
self.addEventListener('load', evt => { | ||
self.parent.postMessage({ | ||
origin: '%s', | ||
referer: '%s', | ||
'sec-fetch-site': '%s', | ||
'sec-fetch-mode': '%s', | ||
'sec-fetch-dest': '%s', | ||
}); | ||
}); | ||
</script>""" % (request.headers.get( | ||
b"origin", b"not set"), request.headers.get(b"referer", b"not set"), | ||
request.headers.get(b"sec-fetch-site", b"not set"), | ||
request.headers.get(b"sec-fetch-mode", b"not set"), | ||
request.headers.get(b"sec-fetch-dest", b"not set")) |
22 changes: 22 additions & 0 deletions
22
service-workers/service-worker/resources/same-site-cookies-register.html
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,22 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"/> | ||
<script> | ||
async function onLoad() { | ||
const scope = self.origin + '/cookies/resources/postToParent.py?with-sw'; | ||
const script = './fetch-rewrite-worker.js'; | ||
const reg = await navigator.serviceWorker.register(script, { scope: scope }); | ||
await new Promise(resolve => { | ||
const worker = reg.installing; | ||
worker.addEventListener('statechange', evt => { | ||
if (worker.state === 'activated') { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
if (reg.navigationPreload) { | ||
await reg.navigationPreload.enable(); | ||
} | ||
window.opener.postMessage({ type: 'SW-REGISTERED' }, '*'); | ||
} | ||
self.addEventListener('load', onLoad); | ||
</script> |
11 changes: 11 additions & 0 deletions
11
service-workers/service-worker/resources/same-site-cookies-unregister.html
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,11 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"/> | ||
<script> | ||
async function onLoad() { | ||
const scope = self.origin + '/cookies/resources/postToParent.py?with-sw'; | ||
const reg = await navigator.serviceWorker.getRegistration(scope); | ||
await reg.unregister(); | ||
window.opener.postMessage({ type: 'SW-UNREGISTERED' }, '*'); | ||
} | ||
self.addEventListener('load', onLoad); | ||
</script> |
215 changes: 215 additions & 0 deletions
215
service-workers/service-worker/same-site-cookies.https.html
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,215 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"/> | ||
<title>Service Worker: Same-site cookie behavior</title> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/common/get-host-info.sub.js"></script> | ||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> | ||
<script src="/cookies/resources/cookie-helper.sub.js"></script> | ||
<body> | ||
<script> | ||
'use strict'; | ||
async function unregister_service_worker(origin) { | ||
const w = window.open(origin + | ||
'/service-workers/service-worker/resources/same-site-cookies-unregister.html'); | ||
try { | ||
await wait_for_message('SW-UNREGISTERED'); | ||
} finally { | ||
w.close(); | ||
} | ||
} | ||
|
||
async function register_service_worker(origin) { | ||
const w = window.open(origin + | ||
'/service-workers/service-worker/resources/same-site-cookies-register.html'); | ||
try { | ||
await wait_for_message('SW-REGISTERED'); | ||
} finally { | ||
w.close(); | ||
} | ||
} | ||
|
||
async function run_test(t, origin, navaction, swaction, expected) { | ||
const value = 'COOKIE_VALUE'; | ||
await resetSameSiteCookies(origin, value); | ||
if (swaction === 'navpreload') { | ||
assert_true('navigationPreload' in ServiceWorkerRegistration.prototype, | ||
'navigation preload must be supported'); | ||
} | ||
const sw_param = swaction === 'no-sw' ? 'no-sw' : 'with-sw'; | ||
let action_param = ''; | ||
if (swaction === 'fallback') { | ||
action_param = '&ignore'; | ||
} else if (swaction !== 'no-sw') { | ||
action_param = '&' + swaction; | ||
} | ||
const navpreload_param = swaction === 'navpreload' ? '&navpreload' : ''; | ||
const change_request_param = swaction === 'change-request' ? '&change-request' : ''; | ||
const target_string = origin + `/cookies/resources/postToParent.py?` + | ||
`${sw_param}${action_param}` | ||
const target_url = new URL(target_string); | ||
if (navaction === 'window.open') { | ||
const w = window.open(target_url); | ||
t.add_cleanup(() => w.close()); | ||
} else if (navaction === 'form post') { | ||
const poster_url = | ||
`./resources/form-poster.html?target=${encodeURIComponent(target_url)}`; | ||
const w = window.open(poster_url); | ||
t.add_cleanup(() => w.close()); | ||
} | ||
const result = await wait_for_message('COOKIES'); | ||
verifySameSiteCookieState(expected, value, result.data, | ||
DomSameSiteStatus.SAME_SITE); | ||
} | ||
|
||
promise_test(async t => { | ||
await register_service_worker(self.origin); | ||
await register_service_worker(SECURE_SUBDOMAIN_ORIGIN); | ||
await register_service_worker(SECURE_CROSS_SITE_ORIGIN); | ||
}, 'Setup service workers'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'window.open', 'no-sw', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, window.open with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'window.open', 'fallback', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, window.open with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'window.open', 'passthrough', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, window.open with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'window.open', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, window.open with change-request'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'window.open', 'navpreload', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, window.open with navpreload'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'no-sw', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, window.open with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'fallback', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, window.open with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'passthrough', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, window.open with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, window.open with change-request'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'navpreload', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, window.open with navpreload'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'no-sw', | ||
SameSiteStatus.LAX); | ||
}, 'cross-site, window.open with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'fallback', | ||
SameSiteStatus.LAX); | ||
}, 'cross-site, window.open with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'passthrough', | ||
SameSiteStatus.LAX); | ||
}, 'cross-site, window.open with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'cross-site, window.open with change-request'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'navpreload', | ||
SameSiteStatus.LAX); | ||
}, 'cross-site, window.open with navpreload'); | ||
|
||
// | ||
// Form POST tests | ||
// | ||
promise_test(t => { | ||
return run_test(t, self.origin, 'form post', 'no-sw', SameSiteStatus.STRICT); | ||
}, 'same-origin, form post with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'form post', 'fallback', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, form post with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'form post', 'passthrough', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, form post with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, self.origin, 'form post', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'same-origin, form post with change-request'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'no-sw', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, form post with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'fallback', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, form post with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'passthrough', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, form post with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'same-site, form post with change-request'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'no-sw', | ||
SameSiteStatus.CROSS_SITE); | ||
}, 'cross-site, form post with no service worker'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'fallback', | ||
SameSiteStatus.CROSS_SITE); | ||
}, 'cross-site, form post with fallback'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'passthrough', | ||
SameSiteStatus.CROSS_SITE); | ||
}, 'cross-site, form post with passthrough'); | ||
|
||
promise_test(t => { | ||
return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'change-request', | ||
SameSiteStatus.STRICT); | ||
}, 'cross-site, form post with change-request'); | ||
|
||
promise_test(async t => { | ||
await unregister_service_worker(self.origin); | ||
await unregister_service_worker(SECURE_SUBDOMAIN_ORIGIN); | ||
await unregister_service_worker(SECURE_CROSS_SITE_ORIGIN); | ||
}, 'Cleanup service workers'); | ||
|
||
</script> | ||
</body> |