Skip to content

Commit

Permalink
[DIP] Pass the reporter to URLloaderFactory
Browse files Browse the repository at this point in the history
This CL passes an endpoint of the DocumentIsolationPolicyReporter
created after a navigation to the URLLoaderFactory used to load
subresources for the document. This allows CORP checks made for such
load to report resource blocked by DocumentIsolationPolicy.

This CL is part of a chain of CLs implementing DocumentIsolationPolicy
reporting:
1) Add DocumentIsolationPolicyReporter
2) Implement DocumentIsolationPolicyReporter
3) Pass the reporter to URLloaderFactory <- you are here
4) Plumb DIP reporter into workers
5) Plumb DIP reporter in CacheStorage

Bug: 333029815
Change-Id: If6cbf2c7a7cd5eb0dd5e7e287815005cfea7e820
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6074440
Reviewed-by: Kenichi Ishibashi <[email protected]>
Reviewed-by: Arthur Sonzogni <[email protected]>
Commit-Queue: Camille Lamy <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1407813}
  • Loading branch information
Camille Lamy authored and chromium-wpt-export-bot committed Jan 17, 2025
1 parent 9183b01 commit ec3b824
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!doctype html>
<html>
<meta name="timeout" content="long">
<body>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
const BASE = new URL("resources", location).pathname
const FRAME_URL = `${ORIGIN}/common/blank.html` +
'?pipe=header(document-isolation-policy,isolate-and-require-corp)' +
`|header(document-isolation-policy-report-only,isolate-and-require-corp)`;
const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
'?pipe=header(document-isolation-policy,isolate-and-require-corp)' +
`|header(document-isolation-policy-report-only,isolate-and-require-corp)`;
const REPORTING_FRAME_URL = `${ORIGIN}${BASE}/reporting-empty-frame.html` +
'?pipe=header(document-isolation-policy,isolate-and-require-corp)' +
`|header(document-isolation-policy-report-only,isolate-and-require-corp)`;

async function observeReports(global, expected_count) {
const reports = [];
const receivedEveryReports = new Promise(resolve => {
if (expected_count == 0)
resolve();

const observer = new global.ReportingObserver((rs) => {
for (const r of rs) {
reports.push(r.toJSON());
}
if (expected_count <= reports.length)
resolve();
});
observer.observe();

});

await receivedEveryReports;
// Wait 500ms more to catch additionnal unexpected reports.
await new Promise(r => step_timeout(r, 500));
return reports;
}

function checkReport(report, contextUrl, blockedUrl, disposition, destination) {
assert_equals(report.type, 'dip');
assert_equals(report.url, contextUrl);
assert_equals(report.body.type, 'corp');
assert_equals(report.body.blockedURL, blockedUrl);
assert_equals(report.body.disposition, disposition);
assert_equals(report.body.destination, destination);
}

async function fetchInFrame(t, frameUrl, url, expected_count) {
const frame = await with_iframe(frameUrl);
t.add_cleanup(() => frame.remove());

const init = { mode: 'no-cors', cache: 'no-store' };
let future_reports = observeReports(frame.contentWindow, expected_count);
await frame.contentWindow.fetch(url, init).catch(() => {});

return await future_reports;
}

async function fetchInWorker(workerOrPort, url) {
const script =
`fetch('${url}', {mode: 'no-cors', cache: 'no-store'}).catch(() => {});`;
const mc = new MessageChannel();
workerOrPort.postMessage({script, port: mc.port2}, [mc.port2]);
return (await new Promise(r => mc.port1.onmessage = r)).data;
}

// We want to test several URLs in various environments (document,
// dedicated worker, shared worser, service worker). As expectations
// are independent of environment except for the context URLs in reports,
// we define ENVIRONMENTS and CASES to reduce the code duplication.
//
// ENVIRONMENTS is a list of dictionaries. Each dictionary consists of:
// - tag: the name of the environment
// - contextUrl: the URL of the environment settings object
// - run: an async function which generates reports
// - test: a testharness Test object
// - url: the URL for a test case (see below)
//
// CASES is a list of test cases. Each test case consists of:
// - name: the name of the test case
// - url: the URL of the test case
// - check: a function to check the results
// - reports: the generated reports
// - url: the URL of the test case
// - contextUrl: the URL of the environment settings object (see
// ENVORONMENTS)

const ENVIRONMENTS = [{
tag: 'document',
contextUrl: FRAME_URL,
run: async (test, url, expected_count) => {
return await fetchInFrame(test, FRAME_URL, url, expected_count);
},
}];

const CASES = [{
name: 'same-origin',
url: '/common/text-plain.txt',
expected_count: 0,
check: (reports, url, contextUrl) => {}
}, {
name: 'blocked by CORP: same-origin',
url: `${REMOTE_ORIGIN}/common/blank.html?pipe=` +
`header(cross-origin-resource-policy, same-origin)`,
expected_count: 0,
check: (reports, url, contextUrl) => {}
}, {
name: 'blocked due to DIP',
url: `${REMOTE_ORIGIN}/common/text-plain.txt`,
expected_count: 2,
check: (reports, contextUrl, url) => {
checkReport(reports[0], contextUrl, url, 'reporting', '');
checkReport(reports[1], contextUrl, url, 'enforce', '');
}
}, {
name: 'blocked during redirect',
url: `${ORIGIN}/common/redirect.py?location=` +
encodeURIComponent(`${REMOTE_ORIGIN}/common/text-plain.txt`),
expected_count: 2,
check: (reports, contextUrl, url) => {
checkReport(reports[0], contextUrl, url, 'reporting', '');
checkReport(reports[1], contextUrl, url, 'enforce', '');
},
}];

for (const env of ENVIRONMENTS) {
for (const testcase of CASES) {
promise_test(async (t) => {
const reports = await env.run(
t, testcase.url, testcase.expected_count);

assert_equals(reports.length, testcase.expected_count);
testcase.check(reports, env.contextUrl, testcase.url);
}, `[${env.tag}] ${testcase.name}`);
}
}

// A test for a non-empty destination.
promise_test(async (t) => {
const frame = await with_iframe(FRAME_URL);
t.add_cleanup(() => frame.remove());

const url = `${REMOTE_ORIGIN}/common/utils.js`;
const script = frame.contentDocument.createElement('script');
script.src = url;
const future_reports = observeReports(frame.contentWindow, 2);
frame.contentDocument.body.appendChild(script);

const reports = await future_reports;
assert_equals(reports.length, 2);
checkReport(reports[0], FRAME_URL, url, 'reporting', 'script');
checkReport(reports[1], FRAME_URL, url, 'enforce', 'script');
}, 'destination: script');

</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document-Isolation-Policy: isolate-and-require-corp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
HTTP/1.1 200 OK
Content-Type: text/html
document-isolation-policy: isolate-and-require-corp; foo="
document-isolation-policy: "; report-to="endpoint"
document-isolation-policy-report-only: isolate-and-require-corp; foo="
document-isolation-policy-report-only: "; report-to="report-only-endpoint"
Reporting-Endpoints: endpoint="https://{{host}}:{{ports[https][0]}}//html/document-isolation-policy/resources/report.py?key=4d8b6d86-c9a8-47c1-871b-111169a8f79c", report-only-endpoint="/html/document-isolation-policy/resources/report.py?key=5d7c1e33-ef88-43c2-9ca3-c67ff300b8c2"

<!doctype html>
<meta charset="utf-8">
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!doctype html>
<html>
<body>
</body>
</html>

0 comments on commit ec3b824

Please sign in to comment.