tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>