From 59b5ac9bb073a97e2f5d83514f407fd360382b13 Mon Sep 17 00:00:00 2001 From: arthursonzogni Date: Tue, 30 Jun 2020 15:48:02 +0000 Subject: [PATCH] [COOP] access reporting [1/N] List the accesses. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of this is put behind a flag disabled by default. This is mostly based on the initial prototype: https://chromium-review.googlesource.com/c/chromium/src/+/2223934/24 This patches list all the potential accesses to be checked and reported. The CrossOriginOpenerPolicyReporter is preparing itself to install all the CoopAccessMonitor to the renderer(s), but It doesn't send them for now. To write a meaningful patch, this will need accesses to COOP-Report-Only headers and the virtual browsing context group. Explainer [WIP]: https://github.com/camillelamy/explainers/blob/master/coop_reporting.md Specification [WIP]: https://github.com/whatwg/html/pull/5518 Tests [WIP]: https://wpt.fyi/results/html/cross-origin-opener-policy/access-reporting Doc [WIP]: https://docs.google.com/document/d/1H8Be0w27fKPXKqyuJj9oEqIJEjB9Rw5AP3x-w-Fx2Zg/edit?usp=sharing Bug: chromium:1090273 Change-Id: I5d4c613a671f99ba15b4b174431d8d3ddeaa44c6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2264294 Reviewed-by: Camille Lamy Reviewed-by: Pâris Meuleman Commit-Queue: Arthur Sonzogni Cr-Commit-Position: refs/heads/master@{#783972} --- .../frame_host/render_frame_host_impl.cc | 3 + .../cross_origin_opener_policy_reporter.cc | 92 +++++++++++++++++++ .../net/cross_origin_opener_policy_reporter.h | 9 ++ 3 files changed, 104 insertions(+) diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 1ff08abe1a0307..7b326cc27f848b 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc @@ -8211,6 +8211,9 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal( RecordCrossOriginIsolationMetrics(this); + CrossOriginOpenerPolicyReporter::InstallAccessMonitorsIfNeeded( + frame_tree_node_); + return true; } diff --git a/content/browser/net/cross_origin_opener_policy_reporter.cc b/content/browser/net/cross_origin_opener_policy_reporter.cc index 59b0278c737e1d..e178f4f1bbd59e 100644 --- a/content/browser/net/cross_origin_opener_policy_reporter.cc +++ b/content/browser/net/cross_origin_opener_policy_reporter.cc @@ -7,8 +7,13 @@ #include "base/values.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/frame_host/render_frame_proxy_host.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/site_instance.h" #include "content/public/browser/storage_partition.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/mojom/network_context.mojom.h" +#include "url/origin.h" namespace content { @@ -64,6 +69,32 @@ RenderFrameHostImpl* GetSourceRfhForCoopReporting( return current_rfh; } +// Find all the related windows that might try to access the new document in +// |frame|, but are in a different virtual browsing context group. +std::vector CollectOtherWindowForCoopAccess( + FrameTreeNode* frame) { + DCHECK(frame->IsMainFrame()); + SiteInstance* site_instance = frame->current_frame_host()->GetSiteInstance(); + + std::vector out; + for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) { + RenderFrameHostImpl* rfh = wc->GetMainFrame(); + + // Filters out windows from a different browsing context group. + if (!rfh->GetSiteInstance()->IsRelatedSiteInstance(site_instance)) + continue; + + // TODO(arthursonzogni): Filter out window from the same virtual browsing + // context group. + FrameTreeNode* ftn = rfh->frame_tree_node(); + if (ftn == frame) + continue; + + out.push_back(ftn); + } + return out; +} + } // namespace CrossOriginOpenerPolicyReporter::CrossOriginOpenerPolicyReporter( @@ -187,4 +218,65 @@ GURL CrossOriginOpenerPolicyReporter::GetNextDocumentUrlForReporting( return GURL(); } +// static +void CrossOriginOpenerPolicyReporter::InstallAccessMonitorsIfNeeded( + FrameTreeNode* frame) { + if (!frame->IsMainFrame()) + return; + + // The function centralize all the CoopAccessMonitor being added. Checking the + // flag here ensures the feature to be properly disabled everywhere. + if (!base::FeatureList::IsEnabled( + network::features::kCrossOriginOpenerPolicyAccessReporting)) { + return; + } + + // TODO(arthursonzogni): It is too late to update the SiteInstance of the new + // document. Ideally, this should be split into two parts: + // - CommitNavigation: Update the new document's SiteInstance. + // - DidCommitNavigation: Update the other SiteInstances. + + // Find all the related windows that might try to access the new document, + // but are from a different virtual browsing context group. + std::vector other_main_frames = + CollectOtherWindowForCoopAccess(frame); + + CrossOriginOpenerPolicyReporter* reporter_frame = + frame->current_frame_host()->coop_reporter(); + + for (FrameTreeNode* other : other_main_frames) { + CrossOriginOpenerPolicyReporter* reporter_other = + other->current_frame_host()->coop_reporter(); + + // If the current frame has a reporter, install the access monitors to + // monitor the accesses between this frame and the other frame. + if (reporter_frame) + reporter_frame->MonitorAccessesInBetweenWindows(frame, other); + + // If the other frame has a reporter, install the access monitors to monitor + // the accesses between this frame and the other frame. + if (reporter_other) + reporter_other->MonitorAccessesInBetweenWindows(frame, other); + } +} + +void CrossOriginOpenerPolicyReporter::MonitorAccessesInBetweenWindows( + FrameTreeNode* A, + FrameTreeNode* B) { + DCHECK_NE(A, B); + DCHECK(A->current_frame_host()->coop_reporter() == this || + B->current_frame_host()->coop_reporter() == this); + // TODO(arthursonzogni): DCHECK same browsing context group. + // TODO(arthursonzogni): DCHECK different virtual browsing context group. + + // Accesses are made either from the main frame or its same-origin iframes. + // Accesses from the cross-origin ones aren't reported. + // + // It means all the accessed from the first window are made from documents + // inside the same SiteInstance. Only one SiteInstance has to be updated. + + // TODO(arthursonzogni): Continue the implementation. Send an IPC toward the + // accessed window. +} + } // namespace content diff --git a/content/browser/net/cross_origin_opener_policy_reporter.h b/content/browser/net/cross_origin_opener_policy_reporter.h index db91781e89ce07..da2fc9927133f6 100644 --- a/content/browser/net/cross_origin_opener_policy_reporter.h +++ b/content/browser/net/cross_origin_opener_policy_reporter.h @@ -17,6 +17,7 @@ namespace content { +class FrameTreeNode; class StoragePartition; class RenderFrameHostImpl; @@ -62,6 +63,11 @@ class CONTENT_EXPORT CrossOriginOpenerPolicyReporter final const std::vector& redirect_chain, const GlobalFrameRoutingId& initiator_routing_id); + // For every other window in the same browsing context group, but in a + // different virtual browsing context group, install the necessary + // CoopAccessMonitor. The first window is identified by |node|. + static void InstallAccessMonitorsIfNeeded(FrameTreeNode* node); + void Clone( mojo::PendingReceiver receiver) override; @@ -78,6 +84,9 @@ class CONTENT_EXPORT CrossOriginOpenerPolicyReporter final const network::CrossOriginOpenerPolicy& coop, const network::CrossOriginEmbedderPolicy& coep); + // Install the CoopAccessMonitors in between the two windows |A| and |B|. + void MonitorAccessesInBetweenWindows(FrameTreeNode* A, FrameTreeNode* B); + // See the class comment. StoragePartition* const storage_partition_; GURL source_url_;