Chrome · WebProtect › Data Controls
CVE-2026-113299
Memory Corruption in WebProtect (Data Controls)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc |
modified |
Files Changed
chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.ccchrome/browser/enterprise/data_protection/data_protection_clipboard_utils.ccchrome/browser/enterprise/data_protection/print_utils.ccchrome/browser/enterprise/data_protection/data_protection_utils.hchrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.ccchrome/browser/enterprise/BUILD.gncontent/public/browser/clipboard_types.ccchrome/browser/enterprise/data_protection/data_protection_utils.ccchrome/browser/enterprise/data_protection/data_protection_navigation_observer.ccchrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h
Patch
From fc9908e21ed6bdeb36c6ea8b4bacef92357aa231 Mon Sep 17 00:00:00 2001
From: Dominique Fauteux-Chapleau <domfc@chromium.org>
Date: Thu, 24 Sep 2026 12:12:18 -0700
Subject: [PATCH] [Data Controls] Use precursor origin URL for blank/uncommitted frames
When a popup is opened with a non-committing URL such as
`window.open("javascript:void(0)")`, the popup remains on its initial
empty document and `RenderFrameHost::GetLastCommittedURL()` returns an
empty/invalid `GURL`. Similarly, `window.open("about:blank")` commits
`about:blank` as its URL while inheriting the opener's security origin.
Because Data Controls and Enterprise Connectors checks relied solely on
`GetLastCommittedURL()`, popups on an initial empty document or
`about:blank` could bypass site-specific rules (e.g. `urls:
["source.com"]`) as well as wildcard rules (`urls: ["*"]`, since
`url_matcher::URLMatcher` does not match invalid `GURL`s and
`CreateDataEndpoint()` returned `std::nullopt` before invoking
`MaybeOverrideSourceURLForClipboardAccess()`).
This CL:
1. Introduces a shared `GetUrlFromRenderFrameHost()` helper in
`enterprise_data_protection` that resolves the main frame's last
committed URL, falling back to
`GetLastCommittedOrigin().GetTupleOrPrecursorTupleIfOpaque().GetURL()`
(or `about:blank` when no valid origin tuple exists) when the
committed URL is invalid, `about:blank`, or `about:srcdoc`.
2. Updates `MaybeOverrideSourceURLForClipboardAccess()`,
`DataProtectionNavigationObserver::ApplyDataProtectionSettings()`,
and `GetPrintAnalysisData()` to use `GetUrlFromRenderFrameHost()`.
3. Moves the `!source_url.is_valid()` check in
`content::CreateDataEndpoint()` to run after
`MaybeOverrideSourceURLForClipboardAccess()` so uncommitted documents
can still receive an overridden `DataTransferEndpoint` URL.
Fixed: 532987588
Change-Id: I7726712b0d6ed51f38b9fe51db56ef00d7db439e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8424128
Reviewed-by: Avi Drissman <avi@chromium.org>
Auto-Submit: Dominique Fauteux-Chapleau <domfc@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Commit-Queue: Dominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1704494}
---
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
index bb419ed..66938bba 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
@@ -24,9 +24,9 @@
#include "chrome/browser/enterprise/data_controls/chrome_clipboard_context.h"
#include "chrome/browser/enterprise/data_controls/chrome_rules_service.h"
#include "chrome/browser/enterprise/data_controls/data_controls_dialog_factory.h"
+#include "chrome/browser/enterprise/data_protection/data_protection_utils.h"
#include "chrome/browser/enterprise/data_protection/paste_allowed_request.h"
#include "chrome/browser/profiles/profile.h"
-#include "components/dom_distiller/core/url_utils.h"
#include "components/enterprise/common/files_scan_data.h"
#include "components/enterprise/connectors/core/connectors_prefs.h"
#include "components/enterprise/connectors/core/features.h"
@@ -56,6 +56,7 @@
#include "ui/base/data_transfer_policy/data_transfer_policy_controller.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
+#include "url/url_constants.h"
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
#include "chrome/browser/printing/print_preview_dialog_controller.h"
@@ -95,19 +96,14 @@
if (!endpoint.data_transfer_endpoint() ||
!endpoint.data_transfer_endpoint()->IsUrlType() ||
!endpoint.data_transfer_endpoint()->GetURL()) {
+ if (endpoint.render_frame_host()) {
+ return GetUrlFromRenderFrameHost(endpoint.render_frame_host());
+ }
return GURL();
}
return *endpoint.data_transfer_endpoint()->GetURL();
}
-GURL GetSourceURL(content::RenderFrameHost* rfh) {
- auto url = rfh->GetMainFrame()->GetLastCommittedURL();
- if (dom_distiller::url_utils::IsDistilledPage(url)) {
- url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(url);
- }
- return url;
-}
-
bool SkipDataControlOrContentAnalysisChecks(
const content::ClipboardEndpoint& main_endpoint) {
// Data Controls and content analysis copy/paste checks require an active tab
@@ -405,11 +401,11 @@
Profile::FromBrowserContext(destination->GetBrowserContext())) {
email = enterprise_connectors::ContentAreaUserProvider::GetUser(
profile, content::WebContents::FromRenderFrameHost(destination),
- GetSourceURL(destination));
+ GetUrlFromRenderFrameHost(destination));
}
- router->ReportPasteFromGemini(GetSourceURL(destination), email, verdict,
- content_size, bypassed);
+ router->ReportPasteFromGemini(GetUrlFromRenderFrameHost(destination), email,
+ verdict, content_size, bypassed);
#endif // BUILDFLAG(SAFE_BROWSING_AVAILABLE)
}
@@ -876,7 +872,7 @@
}
content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
- auto url = GetSourceURL(rfh);
+ auto url = GetUrlFromRenderFrameHost(rfh);
if (!url.is_valid()) {
return std::nullopt;
}
@@ -907,7 +903,7 @@
// inside a cross-origin iframe.
if (profile &&
enterprise_connectors::ContentAnalysisDelegate::IsEnabled(
- profile, GetSourceURL(destination), &dialog_data,
+ profile, GetUrlFromRenderFrameHost(destination), &dialog_data,
enterprise_connectors::AnalysisConnector::BULK_DATA_ENTRY)) {
dialog_data.text.push_back(std::move(data));
dialog_data.reason =
@@ -1456,7 +1452,7 @@
}
ui::DataTransferEndpoint dte(
- GetSourceURL(rfh),
+ GetUrlFromRenderFrameHost(rfh),
{.off_the_record = rfh->GetBrowserContext()->IsOffTheRecord()});
content::ClipboardEndpoint clipboard_endpoint =
content::ClipboardEndpoint::ForFrame(
@@ -1511,7 +1507,7 @@
if (rules_service) {
base::ElapsedTimer timer;
auto verdict = rules_service->GetPasteFromGeminiInChromeVerdict(
- GetSourceURL(destination));
+ GetUrlFromRenderFrameHost(destination));
base::UmaHistogramTimes(
"Enterprise.DataControls.GlicPaste.EvaluationLatency",
timer.Elapsed());
@@ -1586,20 +1582,39 @@
std::move(callback));
}
+GURL GetUrlFromRenderFrameHost(content::RenderFrameHost* rfh) {
+ if (!rfh) {
+ return GURL();
+ }
+ auto* main_frame = rfh->GetMainFrame();
+ auto url = main_frame->GetLastCommittedURL();
+ if (!url.is_valid() || url.IsAboutBlank() || url.IsAboutSrcdoc()) {
+ auto origin_url = main_frame->GetLastCommittedOrigin()
+ .GetTupleOrPrecursorTupleIfOpaque()
+ .GetURL();
+ if (origin_url.is_valid()) {
+ url = std::move(origin_url);
+ } else if (!url.is_valid()) {
+ url = GURL(url::kAboutBlankURL);
+ }
+ }
+ return GetOriginalUrl(url);
+}
+
std::optional<GURL> MaybeOverrideSourceURLForClipboardAccess(
content::RenderFrameHost* render_frame_host,
const GURL& original_url) {
DCHECK(render_frame_host);
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
if (printing::PrintPreviewDialogController::IsPrintPreviewURL(original_url)) {
- return printing::PrintPreviewDialogController::GetInstance()
- ->GetInitiator(
- content::WebContents::FromRenderFrameHost(render_frame_host))
- ->GetPrimaryMainFrame()
- ->GetLastCommittedURL();
+ return GetUrlFromRenderFrameHost(
+ printing::PrintPreviewDialogController::GetInstance()
+ ->GetInitiator(
+ content::WebContents::FromRenderFrameHost(render_frame_host))
+ ->GetPrimaryMainFrame());
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
- return std::nullopt;
+ return GetUrlFromRenderFrameHost(render_frame_host);
}
} // namespace enterprise_data_protection
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h
index 79e734c..af037e9f6 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h
@@ -208,8 +208,15 @@
void CopyTextToClipboard(content::RenderFrameHost* rfh,
const std::u16string& text);
-// Returns the initiator main frame's last committed URL if the `original_url`
-// is a Print Preview URL. Returns std::nullopt otherwise.
+// Returns the URL to use for data protection checks for `rfh`. If the main
+// frame's last committed URL is invalid, about:blank, or about:srcdoc, this
+// falls back to the main frame's precursor/tuple origin URL (or about:blank if
+// no valid origin tuple exists), and unwraps distilled page URLs.
+GURL GetUrlFromRenderFrameHost(content::RenderFrameHost* rfh);
+
+// Returns the desired URL for clipboard source/destination reporting for
+// `render_frame_host`, including resolving the initiator URL when
+// `original_url` is a Print Preview URL.
std::optional<GURL> MaybeOverrideSourceURLForClipboardAccess(
content::RenderFrameHost* render_frame_host,
const GURL& original_url);
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.cc
index 6fa3e62..44b40f2 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_types.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h"
#include "chrome/browser/glic/host/guest_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/clipboard_types.h"
@@ -87,6 +88,15 @@
const content::ClipboardEndpoint& source) {
BasicPasteSource cached;
cached.data_transfer_endpoint = source.data_transfer_endpoint();
+ if (!cached.data_transfer_endpoint && source.render_frame_host()) {
+ cached.data_transfer_endpoint = ui::DataTransferEndpoint(
+ GetUrlFromRenderFrameHost(source.render_frame_host()),
+ {
+ .off_the_record = source.render_frame_host()
+ ->GetBrowserContext()
+ ->IsOffTheRecord(),
+ });
+ }
if (source.browser_context()) {
cached.browser_context = source.browser_context()->GetWeakPtr();
}
diff --git a/chrome/browser/enterprise/data_protection/data_protection_navigation_observer.cc b/chrome/browser/enterprise/data_protection/data_protection_navigation_observer.cc
index 32fe9a344..f36d5f2f 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_navigation_observer.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_navigation_observer.cc
@@ -15,14 +15,14 @@
#include "base/time/time.h"
#include "chrome/browser/enterprise/connectors/connectors_service.h"
#include "chrome/browser/enterprise/data_controls/chrome_rules_service.h"
+#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h"
#include "chrome/browser/enterprise/data_protection/data_protection_features.h"
#include "chrome/browser/enterprise/data_protection/data_protection_url_lookup_service_factory.h"
+#include "chrome/browser/enterprise/data_protection/data_protection_utils.h"
#include "chrome/browser/interstitials/enterprise_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
-#include "components/dom_distiller/core/url_constants.h"
-#include "components/dom_distiller/core/url_utils.h"
#include "components/enterprise/data_protection/data_protection_url_lookup_service.h"
#include "components/enterprise/data_protection/utils.h"
#include "components/safe_browsing/buildflags.h"
@@ -98,17 +98,6 @@
}
#endif // BUILDFLAG(SAFE_BROWSING_AVAILABLE)
-GURL GetOriginalUrl(const GURL& url) {
- if (GURL got_url =
- dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(url);
- got_url.is_valid()) {
- return got_url;
- } else {
- VLOG(1) << __func__ << " got a invalid url: " << got_url;
- }
- return url;
-}
-
void RunPendingNavigationCallback(
content::WebContents* web_contents,
DataProtectionNavigationObserver::Callback callback) {
@@ -313,14 +302,15 @@
return;
}
- if (!web_contents->GetLastCommittedURL().is_valid()) {
+ const GURL original_url =
+ GetUrlFromRenderFrameHost(web_contents->GetPrimaryMainFrame());
+ if (!original_url.is_valid()) {
std::move(callback).Run(UrlSettings::None());
return;
}
std::string identifier = GetIdentifier(profile);
- const GURL original_url = GetOriginalUrl(web_contents->GetLastCommittedURL());
DataProtectionPageUserData::UpdateDataControlsScreenshotState(
GetPageFromWebContents(web_contents), identifier,
IsScreenshotAllowedByDataControls(profile, original_url));
diff --git a/chrome/browser/enterprise/data_protection/data_protection_utils.cc b/chrome/browser/enterprise/data_protection/data_protection_utils.cc
new file mode 100644
index 0000000..93490f1
--- /dev/null
+++ b/chrome/browser/enterprise/data_protection/data_protection_utils.cc
@@ -0,0 +1,24 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/data_protection/data_protection_utils.h"
+
+#include "base/logging.h"
+#include "components/dom_distiller/core/url_utils.h"
+#include "url/gurl.h"
+
+namespace enterprise_data_protection {
+
+GURL GetOriginalUrl(const GURL& url) {
+ if (GURL original_url =
+ dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(url);
+ original_url.is_valid()) {
+ return original_url;
+ } else {
+ VLOG(1) << __func__ << " got a invalid url: " << original_url;
+ }
+ return url;
+}
+
+} // namespace enterprise_data_protection
diff --git a/chrome/browser/enterprise/data_protection/data_protection_utils.h b/chrome/browser/enterprise/data_protection/data_protection_utils.h
new file mode 100644
index 0000000..cd9a6fc
--- /dev/null
+++ b/chrome/browser/enterprise/data_protection/data_protection_utils.h
@@ -0,0 +1,20 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_DATA_PROTECTION_DATA_PROTECTION_UTILS_H_
+#define CHROME_BROWSER_ENTERPRISE_DATA_PROTECTION_DATA_PROTECTION_UTILS_H_
+
+#include "url/gurl.h"
+
+namespace enterprise_data_protection {
+
+// Returns the URL that data protection checks should apply to for `url`. If
+// `url` is a distilled page URL (chrome-distiller://), the original URL of the
+// distilled page is returned so that policies apply to the page the content
+// actually comes from. `url` is returned as-is otherwise.
+GURL GetOriginalUrl(const GURL& url);
+
+} // namespace enterprise_data_protection
+
+#endif // CHROME_BROWSER_ENTERPRISE_DATA_PROTECTION_DATA_PROTECTION_UTILS_H_
diff --git a/chrome/browser/enterprise/data_protection/print_utils.cc b/chrome/browser/enterprise/data_protection/print_utils.cc
index cb424c0d6..e578ada 100644
--- a/chrome/browser/enterprise/data_protection/print_utils.cc
+++ b/chrome/browser/enterprise/data_protection/print_utils.cc
@@ -11,6 +11,7 @@
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/memory/read_only_shared_memory_region.h"
+#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "components/device_event_log/device_event_log.h"
#include "components/enterprise/buildflags/buildflags.h"
@@ -149,7 +150,8 @@
bool enabled = enterprise_connectors::ContentAnalysisDelegate::IsEnabled(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
- web_contents->GetOutermostWebContents()->GetLastCommittedURL(),
+ GetUrlFromRenderFrameHost(
+ web_contents->GetOutermostWebContents()->GetPrimaryMainFrame()),
&scanning_data, enterprise_connectors::AnalysisConnector::PRINT);
if (enabled && ShouldScan(context)) {
diff --git a/content/public/browser/clipboard_types.cc b/content/public/browser/clipboard_types.cc
index daa934c..6a0aa70 100644
--- a/content/public/browser/clipboard_types.cc
+++ b/content/public/browser/clipboard_types.cc
@@ -237,10 +237,6 @@
RenderFrameHost& rfh) {
auto* render_frame_host_main_frame = rfh.GetMainFrame();
auto source_url = render_frame_host_main_frame->GetLastCommittedURL();
- if (!source_url.is_valid()) {
- return std::nullopt;
- }
-
if (auto maybe_url = GetContentClient()
->browser()
->MaybeOverrideSourceURLForClipboardAccess(
@@ -248,6 +244,10 @@
source_url = *maybe_url;
}
+ if (!source_url.is_valid()) {
+ return std::nullopt;
+ }
+
return ui::DataTransferEndpoint(
source_url,
ui::DataTransferEndpointOptions{
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc
index 9869109..21eb6e5 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_unittest.cc
@@ -28,6 +28,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/drop_data.h"
#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -1550,4 +1551,116 @@
EXPECT_FALSE(PrepopulateFindBarTextAllowed(source, destination_blocked));
}
+class DataProtectionClipboardInitialEmptyDocumentPopupTest
+ : public DataProtectionClipboardTest {
+ public:
+ void SetUp() override {
+ DataProtectionClipboardTest::SetUp();
+ scoped_features_.InitWithFeatures(
+ {data_controls::kDataControlsSearchWith,
+ enterprise_connectors::kContentAnalysisClipboardCopy},
+ {});
+ opener_web_contents_ =
+ content::WebContentsTester::CreateTestWebContents(profile_, nullptr);
+ content::WebContentsTester::For(opener_web_contents_.get())
+ ->NavigateAndCommit(GURL("https://source.com/page.html"));
+
+ popup_web_contents_ = content::WebContentsTester::CreateTestWebContents(
+ profile_, opener_web_contents_->GetSiteInstance());
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
+ GURL(url::kAboutBlankURL), popup_web_contents_->GetPrimaryMainFrame());
+ simulator->SetInitiatorFrame(opener_web_contents_->GetPrimaryMainFrame());
+ simulator->Commit();
+
+ ASSERT_TRUE(popup_web_contents_->GetLastCommittedURL().IsAboutBlank());
+ ASSERT_EQ(
+ popup_web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
+ url::Origin::Create(GURL("https://source.com")));
+ }
+
+ void SetBlockCopyingFromSourceURLRule() {
+ data_controls::SetDataControls(profile_->GetPrefs(), {R"({
+ "sources": {
+ "urls": ["source.com"]
+ },
+ "restrictions": [
+ {"class": "CLIPBOARD", "level": "BLOCK"}
+ ]
+ })"});
+ }
+
+ void SetBlockCopyingFromWildcardURLRule() {
+ data_controls::SetDataControls(profile_->GetPrefs(), {R"({
+ "sources": {
+ "urls": ["*"]
+ },
+ "restrictions": [
+ {"class": "CLIPBOARD", "level": "BLOCK"}
+ ]
+ })"});
+ }
+
+ protected:
+ content::RenderViewHostTestEnabler test_render_host_factories_;
+ std::unique_ptr<content::WebContents> opener_web_contents_;
+ std::unique_ptr<content::WebContents> popup_web_contents_;
+};
+
+TEST_F(DataProtectionClipboardInitialEmptyDocumentPopupTest,
+ CopyBlockedBySourceURLRule) {
+ SetBlockCopyingFromSourceURLRule();
+
+ auto endpoint = content::CreateClipboardEndpoint(
+ *popup_web_contents_->GetPrimaryMainFrame());
+ ASSERT_TRUE(endpoint.data_transfer_endpoint());
+ ASSERT_TRUE(endpoint.data_transfer_endpoint()->GetURL());
+ EXPECT_EQ(*endpoint.data_transfer_endpoint()->GetURL(),
+ GURL("https://source.com/"));
+
+ base::test::TestFuture<const ui::ClipboardFormatType&,
+ const content::ClipboardPasteData&,
+ std::optional<std::u16string>>
+ future;
+ IsClipboardCopyAllowedByPolicy(endpoint, CopyMetadata(),
+ MakeClipboardPasteData("confidential", "", {}),
+ future.GetCallback());
+ auto data = future.Get<1>();
+ EXPECT_TRUE(data.empty());
+ EXPECT_FALSE(IsClipboardCopyAllowedByPolicyForUI(popup_web_contents_.get()));
+ EXPECT_FALSE(IsSearchWithAllowed(popup_web_contents_.get()));
+ EXPECT_FALSE(CanPopulateFindBarFromSelection(popup_web_contents_.get()));
+}
+
+TEST_F(DataProtectionClipboardInitialEmptyDocumentPopupTest,
+ CopyBlockedByWildcardURLRuleOnUncommittedTabWithoutOpener) {
+ SetBlockCopyingFromWildcardURLRule();
+
+ std::unique_ptr<content::WebContents> uncommitted_web_contents =
+ content::WebContentsTester::CreateTestWebContents(profile_, nullptr);
+ ASSERT_TRUE(uncommitted_web_contents->GetLastCommittedURL().is_empty());
+ ASSERT_TRUE(uncommitted_web_contents->GetPrimaryMainFrame()
+ ->GetLastCommittedOrigin()
+ .opaque());
+
+ auto endpoint = content::CreateClipboardEndpoint(
+ *uncommitted_web_contents->GetPrimaryMainFrame());
+ ASSERT_TRUE(endpoint.data_transfer_endpoint());
+ ASSERT_TRUE(endpoint.data_transfer_endpoint()->GetURL());
+ EXPECT_EQ(*endpoint.data_transfer_endpoint()->GetURL(),
+ GURL(url::kAboutBlankURL));
+
+ base::test::TestFuture<const ui::ClipboardFormatType&,
+ const content::ClipboardPasteData&,
+ std::optional<std::u16string>>
+ future;
+ IsClipboardCopyAllowedByPolicy(endpoint, CopyMetadata(),
+ MakeClipboardPasteData("confidential", "", {}),
+ future.GetCallback());
+ auto data = future.Get<1>();
+ EXPECT_TRUE(data.empty());
+ EXPECT_FALSE(
+ IsClipboardCopyAllowedByPolicyForUI(uncommitted_web_contents.get()));
+ EXPECT_FALSE(IsSearchWithAllowed(uncommitted_web_contents.get()));
+}
+
} // namespace enterprise_data_protection
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page