Skip to content

COEP reporting documentation#39880

Open
hamishwillee wants to merge 8 commits intomdn:mainfrom
hamishwillee:coep_coop_violation_report
Open

COEP reporting documentation#39880
hamishwillee wants to merge 8 commits intomdn:mainfrom
hamishwillee:coep_coop_violation_report

Conversation

@hamishwillee
Copy link
Collaborator

@hamishwillee hamishwillee commented Jun 10, 2025

COEP use the reporting API to report violations. This adds docs.

NOTE - Modified to just cover COEP. Will do COOP in a separate case. The way reporting is done my be slightly different based on https://github.com/mdn/content/pull/39880/files#r2425080573

Doing COEP first because is same as the other types.

Fixes #39814

@github-actions github-actions bot added Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed labels Jun 10, 2025
Comment on lines 12 to 13
> [!NOTE]
> This object does not derive from {{domxref("ReportBody")}} (unlike other {{domxref("Report.body")}} values).
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary because until now we could say that a body was derived from ReportBody. But according to whatwg/html#11365 even if this is true, that will likely be an abstract dictionary.
Further, this object is not named in the spec. I have chosen this name.

FYI if Report also ends up being an abstract base dictionary we might even end up defining this as a "report" rather than a "reportbody". Too early to say.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Report and ReportBody are now specified as dictionaries: https://www.w3.org/TR/reporting-1/#interface-reporting-observer

But this is somewhat aspirational and isn't how they're implemented yet: https://issues.chromium.org/issues/424210695

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yoavweiss Thanks. This is going to make things complicated.

TLDR; I'm probably going to continue documenting this as COEPViolationReportBody and COOPViolationReportBody for now while we wait for the dust to settle. But will discuss in the writing team.


I'm trying to work out what compatibility data should do in mdn/browser-compat-data#27047 (comment) - it is messy because BCD doesn't document dictionaries, but on the other hand, they can only remove things 2 years after they are removed from the web platform. It is going to be confusing for all concerned.

For docs I'm not entirely certain what will happen. Generally Docs don't document dictionaries either, but if we do, we tend to document only the top level dictionaries.
Where I see us going in the longer term is having dictionaries such as CSPViolationReport, COEPViolationReport.
We could do this for the COEP/COOP reports because the spec doesn't indicate any derivation for them at all - it just refers to "an object".

Comment on lines 41 to 43
There are two may causes of violations, which are indicated by the value of the {{domxref("Report.type","type")}} property.
The first is a `navigation` violation, which is caused when an {{htmlelement("iframe")}} or other child browsing context attempts to load a new document or other resource that is not compatible with the embedder policy of its parent for cross origin isolation.
The second is a `"worker initialization"` violation, which is when a page attempts to load a dedicated worker with an embedder policy that is not compatible with the page policy for cross origin isolation.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how I interpret the spec in https://html.spec.whatwg.org/multipage/browsers.html#embedder-policy-checks

Essentially COEP allows you to specify that there must be a resource loading policy or CORS on the thing to be embedded, and whatever it nests.
Ultimately the intent is to ensure that if you care about cross-origin isolation, you'll be able to set a policy that enforces it for emedded resources.

So a violation occurs if the thing that is loading requires cross-origin isolation and the thing to be loaded does not guarantee that.

@yoavweiss Can you sanity check this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies but I don't have a lot of context on this. Again @camillelamy is the authority here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@camillelamy Can you please advise, or suggest someone else who might help. If not I guess I'll have to start adding issues to the spec.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the issue here is that technically COEP applies even if the page does not have cross-origin isolation. I think it'd be better to be very precise about the checks happening:

  • A navigation violation happens when an iframe embedded in a document with either COEP require-corp or COEP credentialless loads a document that:
    • Has neither COEP require-corp or COEP credentialless.
    • Or is cross-origin with the embedder (the one with COEP reporting), and the document does not have a CORP header that allows embedding in the parent.
  • A worker initialization violation happens when a dedicated worker created by a document with either COEP require-corp or COEP credentialless tries to load a worker script with neither COEP require-corp or COEP credentialless headers. Again, I plan to file a spec bug to change this behavior so the COEP is inherited from teh creator and this kind of violation never happens.

@github-actions github-actions bot added the size/l [PR only] 501-1000 LoC changed label Jun 16, 2025

The fields present in the report depend on the type.

## Value
Copy link
Collaborator Author

@hamishwillee hamishwillee Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @yoavweiss

A violation is triggered in a navigation if the policy causes the opened doc to be opened in a new BCG.
The violation report might have a body type of navigation-to-response or navigation-from-response, which are from the perspective of the opened and opener document.

What I think happens here is that if a resource with a COOP header and a reporting endpoint is opened and this causes a BCG switch then it sends a navigation-to-response.
If that same resource is the opener then it sends a navigation-from-response.

YOu don't both reports right - unless both opener and opened have a BCG (but they would then send just their type of message).

@@ -0,0 +1,49 @@
---
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yoavweiss

Do you happen to know the how HTTP enforcement and report-only headers interwork?

I think that

  • you can have Cross-Origin-Opener-Policy and/or Cross-Origin-Opener-Policy-Report-Only headers on any document.
  • The report(s) sent are from the perspective of the document with the policy and reporting endpoint.
  • Violations are determined by comparing that documents policy (which may be enforced or report only or both) and the enforced policy of the other document (you're always comparing the reporting/enforce policy in the current document to the actual policy of the other document).

So say I have an opener with a Cross-Origin-Opener-Policy: same-site that includes a reporting endpoint, and there is a navigation to another document that in this case has no policy (so a policy of unsafe-none and no reporting endpoint)

  • The policy will be evaluated and there is a violation because the opener is opened in a new BCD.
  • As per https://github.com/mdn/content/pull/39880/files#r2151347130 I think a report will be sent to the reporting endpoint of the opener of type navigation-from-response.
  • No report is sent for the opened.

Now consider the case where the opener still sets Cross-Origin-Opener-Policy: same-site but the opened sets Cross-Origin-Opener-Policy-Report-Only: same-site-no-opener and Cross-Origin-Opener-Policy-Report-Only: unsafe-none

  • There is a policy violation because the opened is opened in a new BCG.
  • I think a report will be sent to the reporting endpoint of the opener of type navigation-from-response.
  • I think a two reports will be sent to the reporting endpoint of the "opened" document of type navigation-from-response.
  • The first will be of disposition: enforced, and effectivePolicy unsafe-none
  • The second will be of disposition: reporting, and effectivePolicy same-site-no-opener

I think that is what the spec says, and it makes sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll again have to call on @camillelamy for help here, as I'm not sure what the answer here should be.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I wrote the spec for reporting in COOP and am much more familiar with the implementation, so this one I can answer.

For COOP, we try to see if a browsing context group would happen were we to enforce all the policies we're asked about.

In practice, we separate enforce/report-only mode (which can have different reporting endpoints).

For enforcement, we only compare with the other document enforced policy: we are reporting that a BCG switch did happen after all.

For report-only, this is a bit more complex. First, we check if the other document report-only policy would ensure that there would be no BCG swap. If that's the case, we won't send a report. Our rational is that developers might want to deploy COOP on several documents in their origin in report-only mode first, and we should not warn them about issues that would not happen if they switch all of their report-only policies to enforcement.

If the report-only don't match, then we check what would happen if we enforced the report-only policy of the document we're navigating to compared with the enforced policy of the current document. We also compare the report-only policy of the current document with the enforced policy of the document we're navigating to. If any of those would trigger a BCG swap, then we note that the documents would ned up in two different browsing contexts. We keep track of this information even if the documents navigate again, this is important for the access reporting. When the BCG switch would be triggered, we also send a report to the relevant endpoints of both the current document and the document we're navigating to. If there are no endpoints, then we don't send a response.

In your second example, you listed two report only policies so I am going to assume you meant unsafe-none as the enforced policy and same-origin-no-opener as the report-only one. In that case, you would get:

  • You get a navigation-from-response report on the endpoint of the opener, with disposition enforced and policy same-origin.
  • You get a navigation-to-response report on the enforced endpoint of the openee with disposition enforced and policy unsafe-none.
  • You get a navigation-to-response report on the report-only endpoint of the openee with disposition report-only and policy same-origin-no-opener.

Now if the openee report-only policy was same-origin, and it was same-origin with the opener, you would not get the third report-only report. But you would still get the first two. And if both documents were same-origin and had report-only same-origin COOP, you would not get any report at all. If only one of them had the report-only COOP, then you would get reports.

This gets a bit more complex for access reporting, as we're keeping track of whether any report-only COOP would have triggered a BCG swap. Then when an access happen, we check if the two documents would be in different BCGs if COOP had been enforced. But the BCG swap might have been triggered during a previous navigation. To give you an example:

  • You document has COOP report-only same-origin. It opens a popup with a cross-origin document. We record that all documents in the popup would be in a different BCG from the documents in the opener page if COOP was enforced. Accesses to the popup/from the popup are reported (provided they involve a same-origin document).
  • The popup navigates to another origin. There is no new BCG switch due to COOP, but we would still report accesses because the pages would still be in different BCGs.
  • The opener navigates to a non-COOP page. This would also trigger a BCG switch if COOP was enforced. We record this. However, we no longer have a reporting endpoint for COOP, so we don't send reports anymore.

@github-actions github-actions bot removed the size/m [PR only] 51-500 LoC changed label Jul 9, 2025
@hamishwillee hamishwillee force-pushed the coep_coop_violation_report branch from e1ad482 to f1c6588 Compare August 10, 2025 23:56
@hamishwillee
Copy link
Collaborator Author

FYI, I have not forgotten this. It's just not as high on the priority list as I would like. Won't be this month.

@hamishwillee hamishwillee force-pushed the coep_coop_violation_report branch 2 times, most recently from 411655e to 3538409 Compare October 10, 2025 00:21
@hamishwillee hamishwillee marked this pull request as ready for review November 23, 2025 22:20
@hamishwillee hamishwillee requested review from a team as code owners November 23, 2025 22:20
@hamishwillee hamishwillee requested review from dipikabh and sideshowbarker and removed request for a team November 23, 2025 22:20
@dipikabh dipikabh removed their request for review November 28, 2025 16:07
@dipikabh
Copy link
Contributor

@hamishwillee removing myself for now, please add me back if and when this needs an edit review.

@hamishwillee hamishwillee requested a review from a team as a code owner January 6, 2026 07:13
@github-actions github-actions bot added the Content:HTTP HTTP docs label Jan 6, 2026
@sideshowbarker sideshowbarker requested a review from a team as a code owner February 11, 2026 04:08
@sideshowbarker sideshowbarker force-pushed the coep_coop_violation_report branch from 4672c56 to 1e7d6d9 Compare February 11, 2026 04:09
@sideshowbarker sideshowbarker removed request for a team February 11, 2026 04:33
@hamishwillee hamishwillee force-pushed the coep_coop_violation_report branch from 1e7d6d9 to f0f82d7 Compare February 16, 2026 02:31
@github-actions github-actions bot added size/l [PR only] 501-1000 LoC changed and removed size/m [PR only] 51-500 LoC changed labels Feb 17, 2026
- https://html.spec.whatwg.org/multipage/browsers.html#coep
---

{{APIRef("Reporting API")}} {{non-standard_header}}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, non-standard because it isn't in the spec.

Comment on lines +10 to +11
- https://html.spec.whatwg.org/multipage/browsers.html#embedder-policy-checks
- https://html.spec.whatwg.org/multipage/browsers.html#coep
Copy link
Collaborator Author

@hamishwillee hamishwillee Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, spec URLS here and in other docs until I can get BCD to provide something better.

Comment on lines 24 to 28
- `"navigation"`
- : The violation was triggered by a document attempting to load an {{htmlelement("iframe")}} (of any origin) that does not set a {{httpheader("Cross-Origin-Resource-Policy")}} that allows it to be loaded into the document's origin.
Note that a violation can occur even if the `<iframe>` source is same-origin with the document.
- `"worker initialization"`
- : The violation was caused by a page attempting to load a dedicated worker that does not set a {{httpheader("Cross-Origin-Resource-Policy")}} that allows it to be loaded into the document's origin.
Copy link
Collaborator Author

@hamishwillee hamishwillee Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@camillelamy I believe this to be true, but would appreciate it if you can confirm.

This comes from #39880 (comment)

The reason I ask is that you were talking about making a spec update but I suspect it hasn't happened yet, and even if it has, might not have rolled out to implementations.

FWIW the reporting API indicates that the report should be a dictionary derived from the ReportBody dictionary. The spec would be easier to read if this was the case, and each of the properties had a normative explanation.

@@ -0,0 +1,188 @@
---
title: COEPViolationReportBody
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI According to the current reporting API this should be a dictionary derived from ReportBody dictionary.
Previously ReportBody was an object with no properties and a toJson method, but that has been removed from the spec. However no implementations have yet changed.
That makes all the docs complicated, because we should be able to get rid of the ReportBody abstraction.

Howver the spec does not define this object as a named dictionary - its just an object. I have therefore defined this as a dictionary with a useful name.

Note also that MDN doesn't document dictionaries generally, but it makes sense to do so and continue to do so for this case.

The properties on the object depend on the {{domxref("Report/type", "type")}} of report.
A mapping between the `type` and report body object can be found in [Report types](/en-US/docs/Web/API/Reporting_API#report_types).

Note that the specification defines this property as a {{domxref("ReportBody")}}-derived dictionary, where `ReportBody` has no properties or methods.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI see my comment here https://github.com/mdn/content/pull/39880/changes#r2815443920

This isn't at all useful, but we can't get rid of it yet - unless reviewer would like me to do so :-)

Comment on lines +17 to +20
A string representing the type of the report.
Allowed types include: `coep`, `csp-violation`, `deprecation`, `intervention`, `integrity-violation`.

See [Report types](/en-US/docs/Web/API/Reporting_API#report_types) in _Reporting API_ for more information.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, it is going to be hard to be sure this is all in sync over time. I have therefore cross linked to report types, which is a useful list and mapping of report types to the body type. We'll try keep them in sync, but if not at least there is a master list.

Comment on lines +15 to +19
> [!NOTE]
> See {{HTTPHeader("Cross-Origin-Embedder-Policy")}} for more complete information and examples.
>
> The headers are the same except `Cross-Origin-Embedder-Policy` blocks resources from loading is needed for a document to be [cross-origin isolated](/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy#cross-origin_isolation).
> In addition, [Violation reports](/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy#violation_reports) from `Cross-Origin-Embedder-Policy-Report-Only` have a {{domxref("COEPViolationReportBody/disposition", "disposition")}} of `"reporting"` instead of `enforce`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header is almost the same as the Cross-Origin-Embedder-Policy . Rather than duplicate everything I have kept the syntax and directives, but removed the Description and examples.

This section notes the differences and points you to Cross-Origin-Embedder-Policy

---

The HTTP **`Cross-Origin-Embedder-Policy`** (COEP) {{Glossary("response header")}} configures the current document's policy for loading and embedding cross-origin resources.
The HTTP **`Cross-Origin-Embedder-Policy`** (COEP) {{Glossary("response header")}} configures the current document's policy for loading and embedding cross-origin resources that are requested in `no-cors` mode.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI This has had significant changes.

For the new features I have added a section on violation reporting and an example of just server-side reporting. This links to the reporting API docs - there is some duplication, but I think worth it.

More significantly I have slightly reframed the way this is presented.

This had somewhat mixed the disucssion of CEOP and CORS, which are the mechanisms for a resource to define whether it can be embedded for no-cors and cors requests, respectively.
These are both part of ensuring cross-origin isolation but they are aren't otherwise "linked"

The way it was though, it read like if you have a CORS error and you have set the COEP header, you'd get a COEP voilation. Actually you get a CORS violation, which isn't reported except via console.

Now tis is more clear.

@hamishwillee
Copy link
Collaborator Author

@sideshowbarker @yoavweiss @camillelamy This documentation on COEP and COEP reporting is ready for review.
I'd appreciate some sanity checking if you have time.

Sorry it took so long! I plan on looking at it again on Friday as doubtless there are some typos etc, but the broad story should be right. Note that this borrows from the structure of the other docs - the HTTP headers dicuss the reports, but the detail on how to get them and what the properties are is documented as part of the Reporting API in the relevant body object.

@hamishwillee hamishwillee force-pushed the coep_coop_violation_report branch from cab4395 to 9e9dbee Compare February 17, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:HTTP HTTP docs Content:WebAPI Web API docs size/l [PR only] 501-1000 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

COOP and COEP have no documentation of the report-to parameter

4 participants