subresource-integrity-font.html (9942B)
1 <!DOCTYPE html> 2 <title>Subresource Integrity for font 3 </title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/preload/resources/preload_helper.js"></script> 7 <script src="/common/utils.js"></script> 8 <script src="/common/get-host-info.sub.js"></script> 9 <body> 10 <script> 11 const integrities = { 12 sha256: 'sha256-xkrni1nquuAzPoWieTZ22i9RONF4y11sJyWgYQDVlxE=', 13 sha384: 'sha384-Vif8vpq+J5UhnTqtncDDyol01dZx9nurRqQcSGtlCf0L1G8P+YeTyUYyZn4LMGrl', 14 sha512: 'sha512-CVkJJeS4/8zBdqBHmpzMvbI987MEWpTVd1Y/w20UFU0+NWlJAQpl1d3lIyCF97CQ/N+t/gn4IkWP4pjuWWrg6A==', 15 incorrect_sha256: 'sha256-wrongwrongwrongwrongwrongwrongwrongvalue====', 16 incorrect_sha512: 'sha512-wrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrong===', 17 unknown_algo: 'foo666-8aBiAJl3ukQwSJ6eTs5wl6hGjnOtyXjcTRdAf89uIfY=' 18 }; 19 20 const run_test = (preload_success, main_load_success, name, 21 resource_url, extra_attributes, number_of_requests) => { 22 const test = async_test(name); 23 const link = document.createElement('link'); 24 link.rel = 'preload'; 25 link.as = 'font'; 26 link.href = resource_url; 27 28 for (const attribute_name in extra_attributes) { 29 link[attribute_name] = extra_attributes[attribute_name]; 30 } 31 32 const valid_preload_failed = test.step_func(() => { 33 assert_unreached('Valid preload fired error handler.'); 34 }); 35 const invalid_preload_succeeded = test.step_func(() => { 36 assert_unreached('Invalid preload load succeeded.'); 37 }); 38 const valid_main_load_failed = test.step_func(() => { 39 assert_unreached('Valid main load fired error handler.'); 40 }); 41 const invalid_main_load_succeeded = test.step_func(() => { 42 assert_unreached('Invalid main load succeeded.'); 43 }); 44 const main_load_pass = test.step_func(() => { 45 verifyNumberOfResourceTimingEntries(resource_url, number_of_requests); 46 test.done(); 47 }); 48 49 const preload_pass = test.step_func(async () => { 50 try { 51 await new FontFace('CanvasTest', `url("${resource_url}")`).load(); 52 } catch (error) { 53 if (main_load_success) { 54 valid_main_load_failed(); 55 } else { 56 main_load_pass(); 57 } 58 } 59 60 if (main_load_success) { 61 main_load_pass(); 62 } else { 63 invalid_main_load_succeeded(); 64 } 65 }); 66 67 if (preload_success) { 68 link.onload = preload_pass; 69 link.onerror = valid_preload_failed; 70 } else { 71 link.onload = invalid_preload_succeeded; 72 link.onerror = preload_pass; 73 } 74 75 document.body.appendChild(link); 76 }; 77 78 verifyPreloadAndRTSupport(); 79 80 const anonymous = '&pipe=header(Access-Control-Allow-Origin,*)'; 81 const use_credentials = '&pipe=header(Access-Control-Allow-Credentials,true)|' + 82 'header(Access-Control-Allow-Origin,' + location.origin + ')'; 83 const cross_origin_prefix = get_host_info().REMOTE_ORIGIN; 84 const file_path = '/fonts/CanvasTest.ttf'; 85 86 // Note: About preload + font + CORS 87 // 88 // The CSS Font spec defines that font files always have to be fetched using 89 // anonymous-mode CORS. 90 // 91 // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#cors-enabled_fetches 92 // https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements 93 // 94 // So that font loading (@font-face in CSS and FontFace.load()) always 95 // sends requests with anonymous-mode CORS. The crossOrigin attribute of 96 // <link rel="preload" as="font"> should be set as anonymout mode, 97 // too, even for same origin fetch. Otherwise, main font loading 98 // doesn't match the corresponding preloading due to credentials 99 // mode mismatch and the main font loading invokes another request. 100 101 // Needs CORS request even for same origin preload. 102 run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash.', 103 file_path + '?' + token(), 104 {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1); 105 106 run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha384 hash.', 107 file_path + '?' + token(), 108 {integrity: integrities.sha384, crossOrigin: 'anonymous'}, 1); 109 110 run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha512 hash.', 111 file_path + '?' + token(), 112 {integrity: integrities.sha512, crossOrigin: 'anonymous'}, 1); 113 114 run_test(true, true, '<crossorigin="anonymous"> Same-origin with empty integrity.', 115 file_path + '?' + token(), 116 {integrity: '', crossOrigin: 'anonymous'}, 1); 117 118 run_test(true, true, '<crossorigin="anonymous"> Same-origin with no integrity.', 119 file_path + '?' + token(), 120 {crossOrigin: 'anonymous'}, 1); 121 122 run_test(false, false, '<crossorigin="anonymous"> Same-origin with incorrect hash.', 123 file_path + '?' + token(), 124 {integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1); 125 126 run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash, options.', 127 file_path + '?' + token(), 128 {integrity: `${integrities.sha256}?foo=bar?spam=eggs`, crossOrigin: 'anonymous'}, 1); 129 130 run_test(true, true, '<crossorigin="anonymous"> Same-origin with unknown algorithm only.', 131 file_path + '?' + token(), 132 {integrity: integrities.unknown_algo, crossOrigin: 'anonymous'}, 1); 133 134 run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including correct.', 135 file_path + '?' + token(), 136 {integrity: `${integrities.sha256} ${integrities.incorrect_sha256}`, crossOrigin: 'anonymous'}, 1); 137 138 run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including unknown algorithm.', 139 file_path + '?' + token(), 140 {integrity: `${integrities.sha256} ${integrities.unknown_algo}`, crossOrigin: 'anonymous'}, 1); 141 142 run_test(true, true, '<crossorigin="anonymous"> Same-origin with sha256 mismatch, sha512 match.', 143 file_path + '?' + token(), 144 {integrity: `${integrities.incorrect_sha256} ${integrities.sha512}`, crossOrigin: 'anonymous'}, 1); 145 146 run_test(false, false, '<crossorigin="anonymous"> Same-origin with sha256 match, sha512 mismatch.', 147 file_path + '?' + token(), 148 {integrity: `${integrities.sha256} ${integrities.incorrect_sha512}`, crossOrigin: 'anonymous'}, 1); 149 150 // Main loading shouldn't match preloading due to credentials mode mismatch 151 // so the number of requests should be two. 152 run_test(true, true, 'Same-origin, not CORS request, with correct sha256 hash.', 153 file_path + '?' + token(), 154 {integrity: integrities.sha256}, 2); 155 156 // Main loading shouldn't match preloading due to credentials mode mismatch 157 // and the main loading should invoke another request. The main font loading 158 // always sends CORS request and doesn't support SRI by itself, so it should succeed. 159 run_test(false, true, 'Same-origin, not CORS request, with incorrect sha256 hash.', 160 file_path + '?' + token(), 161 {integrity: integrities.incorrect_sha256}, 2); 162 163 run_test(true, true, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, ACAO: *.', 164 cross_origin_prefix + file_path + '?' + token() + anonymous, 165 {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1); 166 167 run_test(false, false, '<crossorigin="anonymous"> Cross-origin with incorrect sha256 hash, ACAO: *.', 168 cross_origin_prefix + file_path + '?' + token() + anonymous, 169 {integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1); 170 171 run_test(false, false, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, with CORS-ineligible resource.', 172 cross_origin_prefix + file_path + '?' + token(), 173 {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1); 174 175 run_test(false, true, 'Cross-origin, not CORS request, with correct sha256.', 176 cross_origin_prefix + file_path + '?' + token() + anonymous, 177 {integrity: integrities.sha256}, 2); 178 179 run_test(false, true, 'Cross-origin, not CORS request, with incorrect sha256.', 180 cross_origin_prefix + file_path + '?' + token() + anonymous, 181 {integrity: integrities.incorrect_sha256}, 2); 182 183 run_test(true, true, '<crossorigin="anonymous"> Cross-origin with empty integrity.', 184 cross_origin_prefix + file_path + '?' + token() + anonymous, 185 {integrity: '', crossOrigin: 'anonymous'}, 1); 186 187 run_test(true, true, 'Cross-origin, not CORS request, with empty integrity.', 188 cross_origin_prefix + file_path + '?' + token() + anonymous, 189 {integrity: ''}, 2); 190 191 // Non-anonymous mode CORS preload request should mismatch the main load. 192 run_test(true, true, '<crossorigin="use-credentials"> Cross-origin with correct sha256 hash, CORS-eligible.', 193 cross_origin_prefix + file_path + '?' + token() + use_credentials, 194 {integrity: integrities.sha256, crossOrigin: 'use-credentials'}, 2); 195 196 run_test(false, true, '<crossorigin="use-credentials"> Cross-origin with incorrect sha256 hash, CORS-eligible.', 197 cross_origin_prefix + file_path + '?' + token() + use_credentials, 198 {integrity: integrities.incorrect_sha256, crossOrigin: 'use-credentials'}, 2); 199 </script> 200 </body> 201 </html>