Commit e702ebbb authored by Takashi Toyoshima's avatar Takashi Toyoshima Committed by Commit Bot

OOR-CORS: tests and documents for webRequest response header injections

This patch adds test cases that an Extension injects response headers
to deceive CORS checks. If OOR-CORS is enabled, such injections need
'extraHeaders' option to work in expected ways.

Bug: 1000554
Change-Id: Ieef9223b1abd798bea215cfec1fb706aabd0a610
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1792207Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695522}
parent f8648bfe
...@@ -119,12 +119,15 @@ complete nor stable. ...@@ -119,12 +119,15 @@ complete nor stable.
</p> </p>
<p> <p>
<span class="availability">Starting from Chrome 79</span>, header modifications <span class="availability">Starting from Chrome 79</span>, request header
affect Cross-Origin Resource Sharing (CORS) checks. If modified headers for modifications affect Cross-Origin Resource Sharing (CORS) checks. If modified
cross-origin requests do not meet the criteria, it will result in sending a CORS headers for cross-origin requests do not meet the criteria, it will result in
preflight to ask the server if such headers can be accepted. If you really need sending a CORS preflight to ask the server if such headers can be accepted.
to modify headers in a way to violate the CORS protocol, you need to specify If you really need to modify headers in a way to violate the CORS protocol, you
<code>'extraHeaders'</code> in <code>opt_extraInfoSpec</code>. need to specify <code>'extraHeaders'</code> in <code>opt_extraInfoSpec</code>.
On the other hand, response header modifications do not work to deceive CORS
checks. If you need to deceive the CORS protocol, you also need to specify
<code>'extraHeaders'</code> for the response modifications.
</p> </p>
<p> <p>
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
<script> <script>
const hostname = 'cors.example.com'; const hostname = 'cors.example.com';
const origin = location.protocol + '//' + hostname + ':' + location.port; const origin = location.protocol + '//' + hostname + ':' + location.port;
const path = '/extensions/api_test/webrequest/cors/accept'; const path = location.search.substr('?path='.length);
const url = origin + path; const url = origin + '/extensions/api_test/webrequest/cors/' + path;
fetch(url); fetch(url);
</script> </script>
...@@ -35,13 +35,13 @@ function registerOriginListeners( ...@@ -35,13 +35,13 @@ function registerOriginListeners(
onCompletedListener, {urls: [listeningUrlPattern]}); onCompletedListener, {urls: [listeningUrlPattern]});
} }
function registerHeaderInjectionListeners(extraInfoSpec) { function registerRequestHeaderInjectionListeners(extraInfoSpec) {
const beforeSendHeadersListener = callbackPass(details => { const beforeSendHeadersListener = callbackPass(details => {
details.requestHeaders.push({name: 'x-foo', value: 'trigger-preflight'}); details.requestHeaders.push({name: 'x-foo', value: 'trigger-preflight'});
return { requestHeaders: details.requestHeaders }; return {requestHeaders: details.requestHeaders};
}); });
chrome.webRequest.onBeforeSendHeaders.addListener( chrome.webRequest.onBeforeSendHeaders.addListener(
beforeSendHeadersListener, {urls: [listeningUrlPattern]}, extraInfoSpec); beforeSendHeadersListener, {urls: [listeningUrlPattern]}, extraInfoSpec);
// If the 'x-foo' header is injected by |beforeSendHeadersListener| without // If the 'x-foo' header is injected by |beforeSendHeadersListener| without
// 'extraHeaders' and with OOR-CORS being enabled, it triggers CORS // 'extraHeaders' and with OOR-CORS being enabled, it triggers CORS
...@@ -66,6 +66,32 @@ function registerHeaderInjectionListeners(extraInfoSpec) { ...@@ -66,6 +66,32 @@ function registerHeaderInjectionListeners(extraInfoSpec) {
event.addListener( event.addListener(
onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]}); onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]});
} }
function registerResponseHeaderInjectionListeners(extraInfoSpec) {
const headersReceivedListener = details => {
details.responseHeaders.push(
{name: 'Access-Control-Allow-Origin', value: '*'});
return { responseHeaders: details.responseHeaders };
};
chrome.webRequest.onHeadersReceived.addListener(
headersReceivedListener, {urls: [listeningUrlPattern]}, extraInfoSpec);
// If the 'extraHeaders' is not specified and OOR-CORS is enabled, Chrome
// detects CORS failures before |headerReceivedListener| is called and injects
// fake headers to deceive the CORS checks.
const canInjectFakeCorsResponse =
extraInfoSpec.includes('extraHeaders') || getCorsMode() == 'blink';
const event = canInjectFakeCorsResponse ? chrome.webRequest.onCompleted :
chrome.webRequest.onErrorOccurred;
// Wait for the CORS request from the fetch.html to complete.
const onCompletedOrErrorOccurredListener = callbackPass(details => {
chrome.webRequest.onHeadersReceived.removeListener(headersReceivedListener);
event.removeListener(onCompletedOrErrorOccurredListener);
});
event.addListener(
onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]});
}
runTests([ runTests([
function testOriginHeader() { function testOriginHeader() {
...@@ -80,22 +106,38 @@ runTests([ ...@@ -80,22 +106,38 @@ runTests([
registerOriginListeners(['origin'], [], ['requestHeaders', 'extraHeaders']); registerOriginListeners(['origin'], [], ['requestHeaders', 'extraHeaders']);
// Wait for the navigation to complete. // Wait for the navigation to complete.
navigateAndWait( navigateAndWait(getServerURL(
getServerURL('extensions/api_test/webrequest/cors/fetch.html')); 'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
}, },
function testCorsSensitiveHeaderInjectionWithoutExtraHeaders() { function testCorsSensitiveHeaderInjectionWithoutExtraHeaders() {
registerHeaderInjectionListeners(['blocking', 'requestHeaders']); registerRequestHeaderInjectionListeners(['blocking', 'requestHeaders']);
// Wait for the navigation to complete. // Wait for the navigation to complete.
navigateAndWait( navigateAndWait(getServerURL(
getServerURL('extensions/api_test/webrequest/cors/fetch.html')); 'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
}, },
function testCorsSensitiveHeaderInjectionWithExtraHeaders() { function testCorsSensitiveHeaderInjectionWithExtraHeaders() {
registerHeaderInjectionListeners( registerRequestHeaderInjectionListeners(
['blocking', 'requestHeaders', 'extraHeaders']); ['blocking', 'requestHeaders', 'extraHeaders']);
// Wait for the navigation to complete. // Wait for the navigation to complete.
navigateAndWait( navigateAndWait(getServerURL(
getServerURL('extensions/api_test/webrequest/cors/fetch.html')); 'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
},
function testCorsResponseHeaderInjectionWithoutExtraHeaders() {
registerResponseHeaderInjectionListeners(
['blocking', 'responseHeaders']);
// Wait for the navigation to complete.
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=reject'));
},
function testCorsResponseHeaderInjectionWithExtraHeaders() {
registerResponseHeaderInjectionListeners(
['blocking', 'responseHeaders', 'extraHeaders']);
// Wait for the navigation to complete.
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=reject'));
}, },
]); ]);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment