diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
index 1d41ae3de52c7..0a18b61ace1e7 100644
--- a/chrome/browser/webshare/share_service_impl.cc
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -190,6 +190,18 @@ void ShareServiceImpl::Share(const std::string& title,
return;
}
+ // Defense-in-depth: the renderer is supposed to restrict |share_url| to
+ // http(s) or the document's own scheme (NOT its -derived base URL).
+ // Re-validate here so a compromised or buggy renderer cannot ship file:/
+ // chrome:/filesystem: URLs to the OS share sheet.
+ if (!share_url.is_empty() && !share_url.SchemeIsHTTPOrHTTPS() &&
+ share_url.scheme() !=
+ render_frame_host().GetLastCommittedURL().scheme()) {
+ std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
+ ReportBadMessageAndDeleteThis("Disallowed share_url scheme");
+ return;
+ }
+
if (files.size() > kMaxSharedFileCount) {
VLOG(1) << "Share too large: " << files.size() << " files";
std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.cc b/third_party/blink/renderer/modules/webshare/navigator_share.cc
index 71d8b47de2d03..6ee9767bdeca3 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.cc
@@ -92,9 +92,13 @@ bool CanShareInternal(const LocalDOMWindow& window,
if (data.hasUrl()) {
url = window.CompleteURL(data.url());
+ // Compare against the document's own URL scheme, not BaseURL(): the latter
+ // is derived from and is fully attacker-controlled, so an
+ // https page could set and share file:/chrome:
+ // URLs.
if (!url.IsValid() ||
(!url.ProtocolIsInHttpFamily() &&
- url.Protocol() != window.document()->BaseURL().Protocol())) {
+ url.Protocol() != window.Url().Protocol())) {
if (exception_state) {
exception_state->ThrowTypeError("Invalid URL");
}