'CORP Blocking an Obviously Same-Origin Request with CSP sandbox Set

Imagine a site with two documents: index.html and test.jpg, both located at the root. index.html has the following content.

<!DOCTYPE html>
<html lang=en>
        <head>
                <meta charset=utf-8>
                <meta content="text/html; charset=utf-8" http-equiv=content-type>
                <title>Test</title>
        </head>
        <body>
                <p>Hello.</p>
                <img src="/test.jpg">
        </body>
</html>

If served with no security headers, this works just fine. However, with these headers on index.html:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

and this header on test.jpg:

Cross-Origin-Resource-Policy: same-origin

test.jpg will no longer be loaded by Chrome 98. It reports net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin 200 in the console.

Adding the following header to index.html will cause the image to be blocked by Firefox 97 as well:

Content-Security-Policy: default-src 'none'; style-src 'self'; img-src 'self'; prefetch-src 'self'; frame-ancestors 'none'; form-action 'none'; upgrade-insecure-requests; block-all-mixed-content; disown-opener; sandbox; base-uri 'none'

(I'm aware that that's a lot of directives, but I tried to bisect them to what was important and couldn't figure it out.)

What is happening here? https://example.com and https://example.com/test.jpg are the same origin, aren't they? Explicitly navigating to https://example.com/index.html also blocks the loading of test.jpg. What is missing from my understanding of same-origin? Why is there a difference between Chrome and Firefox's handling? Why does adding CSP cause Firefox to start blocking the requests for test.jpg?

In case I missed something, here are the complete set of headers for each request (from cURL):

> GET / HTTP/2
> Host: example.com
> user-agent: curl/7.77.0
> accept: */*
>
< HTTP/2 200
< date: Wed, 16 Feb 2022 04:56:43 GMT
< server: Apache
< via: e3s
< last-modified: Wed, 16 Feb 2022 04:43:18 GMT
< content-length: 226
< x-content-type-options: nosniff
< referrer-policy: same-origin
< content-security-policy: default-src 'none'; style-src 'self'; img-src 'self'; prefetch-src 'self'; frame-ancestors 'none'; form-action 'none'; upgrade-insecure-requests; block-all-mixed-content; disown-opener; sandbox; base-uri 'none'
< cross-origin-embedder-policy: require-corp
< cross-origin-opener-policy: same-origin
< etag: "e2-5d81b49ddfad8"
< accept-ranges: bytes
< vary: Accept-Encoding
< content-type: text/html; charset=UTF-8
<
> GET /test.jpg HTTP/2
> Host: example.com
> user-agent: curl/7.77.0
> accept: */*
>
< HTTP/2 200
< date: Wed, 16 Feb 2022 04:52:19 GMT
< server: Apache
< x-content-type-options: nosniff
< referrer-policy: same-origin
< cross-origin-resource-policy: same-origin
< last-modified: Wed, 16 Feb 2022 04:22:59 GMT
< etag: "18692-5d81b013bbe82"
< accept-ranges: bytes
< content-length: 99986
< cache-control: public, max-age=31557600, immutable
< age: 336
< via: e2s
< content-type: image/jpeg
<


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source