| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 <title>Service Worker: Tainting of responses fetched via SW.</title> | 2 <title>Service Worker: Tainting of responses fetched via SW.</title> |
| 3 <script src="../resources/testharness.js"></script> | 3 <!-- This test makes a large number of requests sequentially. --> |
| 4 <script src="../resources/testharnessreport.js"></script> | 4 <meta name="timeout" content="long"> |
| 5 <script src="../resources/get-host-info.js?pipe=sub"></script> | 5 <script src="/resources/testharness.js"></script> |
| 6 <script src="resources/test-helpers.js"></script> | 6 <script src="/resources/testharnessreport.js"></script> |
| 7 <script src="/common/get-host-info.sub.js"></script> |
| 8 <script src="resources/test-helpers.sub.js"></script> |
| 7 <body> | 9 <body> |
| 8 <script> | 10 <script> |
| 9 var host_info = get_host_info(); | 11 var host_info = get_host_info(); |
| 10 var BASE_ORIGIN = host_info.HTTP_ORIGIN; | 12 var BASE_ORIGIN = host_info.HTTPS_ORIGIN; |
| 11 var OTHER_ORIGIN = host_info.HTTP_REMOTE_ORIGIN; | 13 var OTHER_ORIGIN = host_info.HTTPS_REMOTE_ORIGIN; |
| 12 var BASE_URL = BASE_ORIGIN + base_path() + | 14 var BASE_URL = BASE_ORIGIN + base_path() + |
| 13 'resources/fetch-access-control.php?'; | 15 'resources/fetch-access-control.py?'; |
| 14 var OTHER_BASE_URL = OTHER_ORIGIN + base_path() + | 16 var OTHER_BASE_URL = OTHER_ORIGIN + base_path() + |
| 15 'resources/fetch-access-control.php?'; | 17 'resources/fetch-access-control.py?'; |
| 16 | 18 |
| 17 function frame_fetch(frame, url, mode, credentials) { | 19 function frame_fetch(frame, url, mode, credentials) { |
| 18 return frame.contentWindow.fetch( | 20 var foreignPromise = frame.contentWindow.fetch( |
| 19 new Request(url, {mode: mode, credentials: credentials})); | 21 new Request(url, {mode: mode, credentials: credentials})) |
| 22 |
| 23 // Event loops should be shared between contexts of similar origin, not all |
| 24 // browsers adhere to this expectation at the time of this writing. Incorrect |
| 25 // behavior in this regard can interfere with test execution when the |
| 26 // provided iframe is removed from the document. |
| 27 // |
| 28 // WPT maintains a test dedicated the expected treatment of event loops, so |
| 29 // the following workaround is acceptable in this context. |
| 30 return Promise.resolve(foreignPromise); |
| 20 } | 31 } |
| 21 | 32 |
| 22 function ng_test(frame, url, mode, credentials) { | 33 var login_and_register; |
| 23 return frame_fetch(frame, url, mode, credentials).then( | 34 promise_test(function(t) { |
| 24 function() { | 35 var SCOPE = 'resources/fetch-response-taint-iframe.html'; |
| 25 throw new Error('fetching url:\"' + url + '\" mode:\"' + mode + | 36 var SCRIPT = 'resources/fetch-rewrite-worker.js'; |
| 26 '\" credentials:\"' + credentials + '\" should fail.'); | 37 var registration; |
| 27 }, | 38 |
| 28 function() {}); | 39 login_and_register = login_https(t, host_info.HTTPS_ORIGIN, host_info.HTTPS_
REMOTE_ORIGIN) |
| 40 .then(function() { |
| 41 return service_worker_unregister_and_register(t, SCRIPT, SCOPE); |
| 42 }) |
| 43 .then(function(r) { |
| 44 registration = r; |
| 45 return wait_for_state(t, registration.installing, 'activated'); |
| 46 }) |
| 47 .then(function() { return with_iframe(SCOPE); }) |
| 48 .then(function(f) { |
| 49 // This test should not be considered complete until after the |
| 50 // service worker has been unregistered. Currently, `testharness.js` |
| 51 // does not support asynchronous global "tear down" logic, so this |
| 52 // must be expressed using a dedicated `promise_test`. Because the |
| 53 // other sub-tests in this file are declared synchronously, this |
| 54 // test will be the final test executed. |
| 55 promise_test(function(t) { |
| 56 f.remove(); |
| 57 return registration.unregister(); |
| 58 }, 'restore global state'); |
| 59 |
| 60 return f; |
| 61 }); |
| 62 return login_and_register; |
| 63 }, 'initialize global state'); |
| 64 |
| 65 function ng_test(url, mode, credentials) { |
| 66 promise_test(function(t) { |
| 67 return login_and_register |
| 68 .then(function(frame) { |
| 69 var fetchRequest = frame_fetch(frame, url, mode, credentials); |
| 70 return promise_rejects(t, new TypeError(), fetchRequest); |
| 71 }); |
| 72 }, 'url:\"' + url + '\" mode:\"' + mode + |
| 73 '\" credentials:\"' + credentials + '\" should fail.'); |
| 29 } | 74 } |
| 30 | 75 |
| 31 function ok_test(frame, url, mode, credentials, expected_type, | 76 function ok_test(url, mode, credentials, expected_type, expected_username) { |
| 32 expected_username) { | 77 promise_test(function() { |
| 33 return frame_fetch(frame, url, mode, credentials) | 78 return login_and_register.then(function(frame) { |
| 34 .then(function(res) { | 79 return frame_fetch(frame, url, mode, credentials) |
| 35 assert_equals(res.type, expected_type); | 80 }) |
| 36 return res.text(); | 81 .then(function(res) { |
| 37 }) | 82 assert_equals(res.type, expected_type, 'response type'); |
| 38 .then(function(text) { | 83 return res.text(); |
| 39 if (expected_type == 'opaque') { | 84 }) |
| 40 assert_equals(text, ''); | 85 .then(function(text) { |
| 41 } else { | 86 if (expected_type == 'opaque') { |
| 42 return new Promise(function(resolve) { | 87 assert_equals(text, ''); |
| 43 var report = resolve; | 88 } else { |
| 44 // text must contain report() call. | 89 return new Promise(function(resolve) { |
| 45 eval(text); | 90 var report = resolve; |
| 46 }) | 91 // text must contain report() call. |
| 47 .then(function(result) { | 92 eval(text); |
| 48 assert_equals(result.username, expected_username); | 93 }) |
| 49 }); | 94 .then(function(result) { |
| 50 } | 95 assert_equals(result.username, expected_username); |
| 51 }) | 96 }); |
| 52 .catch(function(reason) { | 97 } |
| 53 throw new Error('fetching url:\"' + url + '\" mode:\"' + mode + | 98 }); |
| 99 }, 'fetching url:\"' + url + '\" mode:\"' + mode + |
| 54 '\" credentials:\"' + credentials + '\" should ' + | 100 '\" credentials:\"' + credentials + '\" should ' + |
| 55 'success. - ' + reason.message); | 101 'succeed.'); |
| 56 }); | |
| 57 } | 102 } |
| 58 | 103 |
| 59 function build_rewrite_url(origin, url, mode, credentials) { | 104 function build_rewrite_url(origin, url, mode, credentials) { |
| 60 return origin + '/?url=' + encodeURIComponent(url) + '&mode=' + mode + | 105 return origin + '/?url=' + encodeURIComponent(url) + '&mode=' + mode + |
| 61 '&credentials=' + credentials + '&'; | 106 '&credentials=' + credentials + '&'; |
| 62 } | 107 } |
| 63 | 108 |
| 64 function for_each_origin_mode_credentials(callback) { | 109 function for_each_origin_mode_credentials(callback) { |
| 65 [BASE_ORIGIN, OTHER_ORIGIN].forEach(function(origin) { | 110 [BASE_ORIGIN, OTHER_ORIGIN].forEach(function(origin) { |
| 66 ['same-origin', 'no-cors', 'cors'].forEach(function(mode) { | 111 ['same-origin', 'no-cors', 'cors'].forEach(function(mode) { |
| 67 ['omit', 'same-origin', 'include'].forEach(function(credentials) { | 112 ['omit', 'same-origin', 'include'].forEach(function(credentials) { |
| 68 callback(origin, mode, credentials); | 113 callback(origin, mode, credentials); |
| 69 }); | 114 }); |
| 70 }); | 115 }); |
| 71 }); | 116 }); |
| 72 } | 117 } |
| 73 | 118 |
| 74 promise_test(function(t) { | 119 ok_test(BASE_URL, 'same-origin', 'omit', 'basic', 'undefined'); |
| 75 var SCOPE = 'resources/fetch-response-taint-iframe.html'; | 120 ok_test(BASE_URL, 'same-origin', 'same-origin', 'basic', 'username2s'); |
| 76 var SCRIPT = 'resources/fetch-rewrite-worker.js'; | 121 ok_test(BASE_URL, 'same-origin', 'include', 'basic', 'username2s'); |
| 77 var frame = undefined; | 122 ok_test(BASE_URL, 'no-cors', 'omit', 'basic', 'undefined'); |
| 123 ok_test(BASE_URL, 'no-cors', 'same-origin', 'basic', 'username2s'); |
| 124 ok_test(BASE_URL, 'no-cors', 'include', 'basic', 'username2s'); |
| 125 ok_test(BASE_URL, 'cors', 'omit', 'basic', 'undefined'); |
| 126 ok_test(BASE_URL, 'cors', 'same-origin', 'basic', 'username2s'); |
| 127 ok_test(BASE_URL, 'cors', 'include', 'basic', 'username2s'); |
| 128 ng_test(OTHER_BASE_URL, 'same-origin', 'omit'); |
| 129 ng_test(OTHER_BASE_URL, 'same-origin', 'same-origin'); |
| 130 ng_test(OTHER_BASE_URL, 'same-origin', 'include'); |
| 131 ok_test(OTHER_BASE_URL, 'no-cors', 'omit', 'opaque'); |
| 132 ok_test(OTHER_BASE_URL, 'no-cors', 'same-origin', 'opaque'); |
| 133 ok_test(OTHER_BASE_URL, 'no-cors', 'include', 'opaque'); |
| 134 ng_test(OTHER_BASE_URL, 'cors', 'omit'); |
| 135 ng_test(OTHER_BASE_URL, 'cors', 'same-origin'); |
| 136 ng_test(OTHER_BASE_URL, 'cors', 'include'); |
| 137 ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit', 'cors', 'undefined'); |
| 138 ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'same-origin', 'cors', |
| 139 'undefined'); |
| 140 ng_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'include'); |
| 141 ok_test(OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + '&ACACredentials=true', |
| 142 'cors', 'include', 'cors', 'username1s') |
| 78 | 143 |
| 79 return login(t, host_info.HTTP_ORIGIN, host_info.HTTP_REMOTE_ORIGIN) | 144 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 80 .then(function() { | 145 var url = build_rewrite_url( |
| 81 return service_worker_unregister_and_register(t, SCRIPT, SCOPE); | 146 origin, BASE_URL, 'same-origin', 'omit'); |
| 82 }) | 147 // Fetch to the other origin with same-origin mode should fail. |
| 83 .then(function(registration) { | 148 if (origin == OTHER_ORIGIN && mode == 'same-origin') { |
| 84 return wait_for_state(t, registration.installing, 'activated'); | 149 ng_test(url, mode, credentials); |
| 85 }) | 150 } else { |
| 86 .then(function() { return with_iframe(SCOPE); }) | 151 // The response type from the SW should be basic |
| 87 .then(function(f) { | 152 ok_test(url, mode, credentials, 'basic', 'undefined'); |
| 88 frame = f; | 153 } |
| 89 var promises = [ | 154 }); |
| 90 ok_test(f, BASE_URL, 'same-origin', 'omit', 'basic', 'undefined'), | |
| 91 ok_test(f, BASE_URL, 'same-origin', 'same-origin', 'basic', | |
| 92 'username1'), | |
| 93 ok_test(f, BASE_URL, 'same-origin', 'include', 'basic', | |
| 94 'username1'), | |
| 95 ok_test(f, BASE_URL, 'no-cors', 'omit', 'basic', 'undefined'), | |
| 96 ok_test(f, BASE_URL, 'no-cors', 'same-origin', 'basic', | |
| 97 'username1'), | |
| 98 ok_test(f, BASE_URL, 'no-cors', 'include', 'basic', 'username1'), | |
| 99 ok_test(f, BASE_URL, 'cors', 'omit', 'basic', 'undefined'), | |
| 100 ok_test(f, BASE_URL, 'cors', 'same-origin', 'basic', 'username1'), | |
| 101 ok_test(f, BASE_URL, 'cors', 'include', 'basic', 'username1'), | |
| 102 ng_test(f, OTHER_BASE_URL, 'same-origin', 'omit'), | |
| 103 ng_test(f, OTHER_BASE_URL, 'same-origin', 'same-origin'), | |
| 104 ng_test(f, OTHER_BASE_URL, 'same-origin', 'include'), | |
| 105 ok_test(f, OTHER_BASE_URL, 'no-cors', 'omit', 'opaque'), | |
| 106 ok_test(f, OTHER_BASE_URL, 'no-cors', 'same-origin', 'opaque'), | |
| 107 ok_test(f, OTHER_BASE_URL, 'no-cors', 'include', 'opaque'), | |
| 108 ng_test(f, OTHER_BASE_URL, 'cors', 'omit'), | |
| 109 ng_test(f, OTHER_BASE_URL, 'cors', 'same-origin'), | |
| 110 ng_test(f, OTHER_BASE_URL, 'cors', 'include'), | |
| 111 ok_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit', 'cors', | |
| 112 'undefined'), | |
| 113 ok_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'same-origin', | |
| 114 'cors', 'undefined'), | |
| 115 ng_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'include'), | |
| 116 ok_test(f, | |
| 117 OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + | |
| 118 '&ACACredentials=true', | |
| 119 'cors', 'include', 'cors', 'username2') | |
| 120 ]; | |
| 121 | 155 |
| 122 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 156 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 123 var url = build_rewrite_url( | 157 var url = build_rewrite_url( |
| 124 origin, BASE_URL, 'same-origin', 'omit'); | 158 origin, BASE_URL, 'same-origin', 'same-origin'); |
| 125 // Fetch to the other origin with same-origin mode should fail. | |
| 126 if (origin == OTHER_ORIGIN && mode == 'same-origin') | |
| 127 return promises.push(ng_test(f, url, mode, credentials)); | |
| 128 // The response type from the SW should be basic | |
| 129 promises.push( | |
| 130 ok_test(f, url, mode, credentials, 'basic', 'undefined')); | |
| 131 }); | |
| 132 | 159 |
| 133 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 160 // Fetch to the other origin with same-origin mode should fail. |
| 134 var url = build_rewrite_url( | 161 if (origin == OTHER_ORIGIN && mode == 'same-origin') { |
| 135 origin, BASE_URL, 'same-origin', 'same-origin'); | 162 ng_test(url, mode, credentials); |
| 136 // Fetch to the other origin with same-origin mode should fail. | 163 } else { |
| 137 if (origin == OTHER_ORIGIN && mode == 'same-origin') | 164 // The response type from the SW should be basic. |
| 138 return promises.push(ng_test(f, url, mode, credentials)); | 165 ok_test(url, mode, credentials, 'basic', 'username2s'); |
| 139 // The response type from the SW should be basic. | 166 } |
| 140 promises.push( | 167 }); |
| 141 ok_test(f, url, mode, credentials, 'basic', 'username1')); | |
| 142 }); | |
| 143 | 168 |
| 144 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 169 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 145 var url = build_rewrite_url( | 170 var url = build_rewrite_url( |
| 146 origin, OTHER_BASE_URL, 'same-origin', 'omit'); | 171 origin, OTHER_BASE_URL, 'same-origin', 'omit'); |
| 147 // The response from the SW should be an error. | 172 // The response from the SW should be an error. |
| 148 promises.push(ng_test(f, url, mode, credentials)); | 173 ng_test(url, mode, credentials); |
| 149 }); | 174 }); |
| 150 | 175 |
| 151 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 176 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 152 var url = build_rewrite_url( | 177 var url = build_rewrite_url( |
| 153 origin, OTHER_BASE_URL, 'no-cors', 'omit'); | 178 origin, OTHER_BASE_URL, 'no-cors', 'omit'); |
| 154 // SW can respond only to no-cors requests. | |
| 155 if (mode != 'no-cors') | |
| 156 return promises.push(ng_test(f, url, mode, credentials)); | |
| 157 // The response type from the SW should be opaque. | |
| 158 promises.push(ok_test(f, url, mode, credentials, 'opaque')); | |
| 159 }); | |
| 160 | 179 |
| 161 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 180 // SW can respond only to no-cors requests. |
| 162 var url = build_rewrite_url( | 181 if (mode != 'no-cors') { |
| 163 origin, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit'); | 182 ng_test(url, mode, credentials); |
| 164 // Fetch to the other origin with same-origin mode should fail. | 183 } else { |
| 165 if (origin == OTHER_ORIGIN && mode == 'same-origin') | 184 // The response type from the SW should be opaque. |
| 166 return promises.push(ng_test(f, url, mode, credentials)); | 185 ok_test(url, mode, credentials, 'opaque'); |
| 167 // The response from the SW should be cors. | 186 } |
| 168 promises.push( | 187 }); |
| 169 ok_test(f, url, mode, credentials, 'cors', 'undefined')); | |
| 170 }); | |
| 171 | 188 |
| 172 for_each_origin_mode_credentials(function(origin, mode, credentials) { | 189 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 173 var url = build_rewrite_url( | 190 var url = build_rewrite_url( |
| 174 origin, | 191 origin, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit'); |
| 175 OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + | 192 |
| 176 '&ACACredentials=true', | 193 // Fetch to the other origin with same-origin mode should fail. |
| 177 'cors', 'include'); | 194 if (origin == OTHER_ORIGIN && mode == 'same-origin') { |
| 178 // Fetch to the other origin with same-origin mode should fail. | 195 ng_test(url, mode, credentials); |
| 179 if (origin == OTHER_ORIGIN && mode == 'same-origin') | 196 } else { |
| 180 return promises.push(ng_test(f, url, mode, credentials)); | 197 // The response from the SW should be cors. |
| 181 // The response from the SW should be cors. | 198 ok_test(url, mode, credentials, 'cors', 'undefined'); |
| 182 promises.push( | 199 } |
| 183 ok_test(f, url, mode, credentials, 'cors', 'username2')); | 200 }); |
| 184 }); | 201 |
| 185 return Promise.all(promises); | 202 for_each_origin_mode_credentials(function(origin, mode, credentials) { |
| 186 }) | 203 var url = build_rewrite_url( |
| 187 .then(function(f) { | 204 origin, |
| 188 frame.remove() | 205 OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + |
| 189 }) | 206 '&ACACredentials=true', |
| 190 .catch(unreached_rejection(t)); | 207 'cors', 'include'); |
| 191 }, 'Verify the tainting of responses fetched via SW'); | 208 // Fetch to the other origin with same-origin mode should fail. |
| 209 if (origin == OTHER_ORIGIN && mode == 'same-origin') { |
| 210 ng_test(url, mode, credentials); |
| 211 } else { |
| 212 // The response from the SW should be cors. |
| 213 ok_test(url, mode, credentials, 'cors', 'username1s'); |
| 214 } |
| 215 }); |
| 192 </script> | 216 </script> |
| 193 </body> | 217 </body> |
| OLD | NEW |