High chrome Authorization Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Translate
DescriptionMissing authorization in Translate
ComponentTranslate
Bug ClassAuthorization Error
Tracker539043243
Fix commit35a08ea9ad2b (chromium/src) +151/-8
CISA KEVNot listed
CreditedM. Fauzan Wijaya (Gh05t666nero)
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc
modified
TranslateManagerPdfBrowserTest
chrome/browser/translate/translate_manager_browsertest.cc
modified
EXPECT_TRUE
chrome/browser/translate/translate_manager_browsertest.cc
modified
ASSERT_TRUE
chrome/browser/translate/translate_manager_browsertest.cc
modified

Files Changed

  • chrome/browser/ui/pdf/BUILD.gn
  • chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc
  • chrome/browser/translate/translate_manager_browsertest.cc
From 35a08ea9ad2b47993333ad759df13468f3b5ae00 Mon Sep 17 00:00:00 2001
From: Paula Gomez <paugomez@google.com>
Date: Wed, 16 Sep 2026 10:30:29 -0700
Subject: [PATCH] Verify that PDF embedder is the active primary main frame

OnDocumentLoadComplete and OnPdfTextExtracted now verify that the PDF
RenderFrameHost is active, the tab's MIME type is application/pdf, and
the direct embedder of the PDF viewer is the WebContents's primary main
frame, to prevent leaking text to any embedding or cross-origin frame.
This change is meant to replace https://crrev.com/1695715 as a fix for
crbug.com/539043243, since the original change broke PDF translation
functionality.

Bug: 489787451, 539043243
Change-Id: Ie005ad8d15bfcc5792b71df986f5ddc714850253
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8408786
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Katie Lu <ktlu@google.com>
Commit-Queue: Katie Lu <ktlu@google.com>
Cr-Commit-Position: refs/heads/main@{#1699026}
---
diff --git a/chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc b/chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc
index 69a0508a..7f05e176 100644
--- a/chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc
+++ b/chrome/browser/ui/pdf/chrome_pdf_document_helper_client.cc
@@ -20,6 +20,7 @@
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/pdf/browser/pdf_document_helper.h"
 #include "components/pdf/browser/pdf_frame_util.h"
+#include "components/pdf/common/constants.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/translate/core/common/translate_features.h"
 #include "components/user_education/common/feature_promo/feature_promo_controller.h"
@@ -43,6 +44,38 @@
              : content::WebContents::FromRenderFrameHost(&render_frame_host);
 }
 
+content::RenderFrameHost* GetEmbedderHost(
+    content::RenderFrameHost& render_frame_host) {
+  if (chrome_pdf::features::IsOopifPdfEnabled()) {
+    return pdf_frame_util::GetEmbedderHost(&render_frame_host);
+  }
+  auto* guest_view =
+      extensions::MimeHandlerViewGuest::FromRenderFrameHost(&render_frame_host);
+  return guest_view ? guest_view->embedder_rfh() : nullptr;
+}
+
+// Returns the primary main frame if `render_frame_host` belongs to an active,
+// full-page PDF, or nullptr otherwise.
+content::RenderFrameHost* GetFullPagePdfMainFrame(
+    content::RenderFrameHost& render_frame_host) {
+  if (!render_frame_host.IsActive()) {
+    return nullptr;
+  }
+
+  content::WebContents* web_contents = GetWebContentsToUse(render_frame_host);
+  if (!web_contents ||
+      web_contents->GetContentsMimeType() != pdf::kPDFMimeType) {
+    return nullptr;
+  }
+
+  content::RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();
+  if (!main_frame || GetEmbedderHost(render_frame_host) != main_frame) {
+    return nullptr;
+  }
+
+  return main_frame;
+}
+
 bool MaybeShowFeaturePromo(const base::Feature& feature,
                            content::WebContents* contents) {
   auto* user_education_interface =
@@ -109,11 +142,12 @@
     }
   }
 
-  if (base::FeatureList::IsEnabled(translate::kEnableTranslatePdf)) {
-    auto* pdf_helper =
-        pdf::PDFDocumentHelper::GetForCurrentDocument(&render_frame_host);
-    if (pdf_helper) {
-      // Get the text of the first page and send it to the render frame for
+  // Only full-page PDFs are eligible for translation.
+  if (base::FeatureList::IsEnabled(translate::kEnableTranslatePdf) &&
+      GetFullPagePdfMainFrame(render_frame_host)) {
+    if (auto* pdf_helper =
+            pdf::PDFDocumentHelper::GetForCurrentDocument(&render_frame_host)) {
+      // Get the text of the first page and send it to the main frame for
       // language detection.
       pdf_helper->GetPageText(
           0, base::BindOnce(&ChromePDFDocumentHelperClient::OnPdfTextExtracted,
@@ -132,12 +166,18 @@
     return;
   }
 
+  content::RenderFrameHost* main_frame =
+      GetFullPagePdfMainFrame(*render_frame_host);
+  if (!main_frame) {
+    return;
+  }
+
   mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame;
-  render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
+  main_frame->GetRemoteAssociatedInterfaces()->GetInterface(
       &chrome_render_frame);
-  // TODO(b/502015383): Use the actual PDF language tag.
+  // TODO(crbug.com/502015383): Use the actual PDF language tag.
   chrome_render_frame->PdfPageCaptured(text, /*pdf_lang=*/"",
-                                       render_frame_host->GetLastCommittedURL());
+                                       main_frame->GetLastCommittedURL());
 }
 
 void ChromePDFDocumentHelperClient::UpdateContentRestrictions(
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 34d721c7..8613314 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -56,6 +56,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "pdf/buildflags.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/events/base_event_utils.h"
@@ -64,6 +65,15 @@
 #include "ui/views/test/button_test_api.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_PDF)
+#include "base/scoped_observation.h"
+#include "base/test/test_future.h"
+#include "components/pdf/browser/pdf_document_helper.h"
+#include "components/translate/content/browser/pdf_translation_coordinator.h"
+#include "components/translate/core/browser/language_state.h"
+#include "components/translate/core/browser/translate_driver.h"
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 namespace translate {
 namespace {
 
@@ -1555,5 +1565,97 @@
 }
 #endif
 
+#if BUILDFLAG(ENABLE_PDF)
+class TranslateManagerPdfBrowserTest : public TranslateManagerBrowserTest {
+ protected:
+  void InitFeatures() override {
+    scoped_feature_list_.InitWithFeatures(
+        {kEnableTranslatePdf, features::kReadAnythingTranslateEntryPoint}, {});
+  }
+};
+
+// Test that loading a full-page PDF initiates language detection and executes
+// `PDFTranslationCoordinator` to determine PDF translatability.
+IN_PROC_BROWSER_TEST_F(TranslateManagerPdfBrowserTest,
+                       FullPagePdfTranslatabilityDetermined) {
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL(
+          "/pdf/accessibility/paragraphs-and-heading-untagged.pdf")));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  ChromeTranslateClient* client = GetChromeTranslateClient();
+  ASSERT_TRUE(client);
+
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return client->GetLanguageState().pdf_translatability_status() !=
+           LanguageState::PdfTranslatabilityStatus::kNotChecked;
+  }));
+
+  EXPECT_EQ(client->GetLanguageState().pdf_translatability_status(),
+            LanguageState::PdfTranslatabilityStatus::kTranslatable);
+
+  auto* coordinator = PDFTranslationCoordinator::GetForCurrentDocument(
+      web_contents->GetPrimaryMainFrame());
+  ASSERT_TRUE(coordinator);
+  EXPECT_EQ(coordinator->status(),
+            PDFTranslationCoordinator::TranslatabilityStatus::kTranslatable);
+}
+
+// Test that loading an HTML page with an embedded PDF drops PDF translation
+// processing (it is not a full-page PDF), so PDF text is never sent to the main
+// frame for language detection and PDF translatability is never checked.
+IN_PROC_BROWSER_TEST_F(TranslateManagerPdfBrowserTest,
+                       EmbeddedPdfDroppedForTranslation) {
+  ChromeTranslateClient* client = GetChromeTranslateClient();
+  ASSERT_TRUE(client);
+
+  struct : TranslateDriver::LanguageDetectionObserver {
+    void OnLanguageDetermined(
+        const LanguageDetectionDetails& details) override {
+      last_contents = details.contents;
+    }
+    std::u16string last_contents;
+  } observer;
+  base::ScopedObservation<TranslateDriver,
+                          TranslateDriver::LanguageDetectionObserver>
+      observation(&observer);
+  observation.Observe(client->GetTranslateDriver());
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/pdf/pdf_embed.html")));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  pdf::PDFDocumentHelper* pdf_helper = nullptr;
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    pdf_helper = pdf::PDFDocumentHelper::MaybeGetForWebContents(*web_contents);
+    return pdf_helper != nullptr;
+  }));
+
+  base::test::TestFuture<void> document_loaded;
+  pdf_helper->RegisterForDocumentLoadComplete(document_loaded.GetCallback());
+  ASSERT_TRUE(document_loaded.Wait());
+
+  // Flush the PDF and renderer Mojo pipes to ensure any PdfPageCaptured() IPC
+  // triggered by OnDocumentLoadComplete() would have completed.
+  base::test::TestFuture<const std::u16string&> page_text;
+  pdf_helper->GetPageText(0, page_text.GetCallback());
+  ASSERT_FALSE(page_text.Get().empty());
+  ASSERT_TRUE(content::ExecJs(web_contents->GetPrimaryMainFrame(), ""));
+
+  EXPECT_NE(observer.last_contents, page_text.Get());
+  EXPECT_EQ(client->GetLanguageState().pdf_translatability_status(),
+            LanguageState::PdfTranslatabilityStatus::kNotChecked);
+  EXPECT_FALSE(PDFTranslationCoordinator::GetForCurrentDocument(
+      web_contents->GetPrimaryMainFrame()));
+}
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 }  // namespace
 }  // namespace translate
diff --git a/chrome/browser/ui/pdf/BUILD.gn b/chrome/browser/ui/pdf/BUILD.gn
index 15be482..6543e88e 100644
--- a/chrome/browser/ui/pdf/BUILD.gn
+++ b/chrome/browser/ui/pdf/BUILD.gn
@@ -22,6 +22,7 @@
     "//chrome/browser/ui/user_education",
     "//chrome/common",
     "//components/pdf/browser",
+    "//components/pdf/common:constants",
     "//components/tabs:public",
     "//extensions/browser",
     "//extensions/browser/mime_handler",
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 34d721c7..8613314 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -56,6 +56,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "pdf/buildflags.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/events/base_event_utils.h"
@@ -64,6 +65,15 @@
 #include "ui/views/test/button_test_api.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_PDF)
+#include "base/scoped_observation.h"
+#include "base/test/test_future.h"
+#include "components/pdf/browser/pdf_document_helper.h"
+#include "components/translate/content/browser/pdf_translation_coordinator.h"
+#include "components/translate/core/browser/language_state.h"
+#include "components/translate/core/browser/translate_driver.h"
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 namespace translate {
 namespace {
 
@@ -1555,5 +1565,97 @@
 }
 #endif
 
+#if BUILDFLAG(ENABLE_PDF)
+class TranslateManagerPdfBrowserTest : public TranslateManagerBrowserTest {
+ protected:
+  void InitFeatures() override {
+    scoped_feature_list_.InitWithFeatures(
+        {kEnableTranslatePdf, features::kReadAnythingTranslateEntryPoint}, {});
+  }
+};
+
+// Test that loading a full-page PDF initiates language detection and executes
+// `PDFTranslationCoordinator` to determine PDF translatability.
+IN_PROC_BROWSER_TEST_F(TranslateManagerPdfBrowserTest,
+                       FullPagePdfTranslatabilityDetermined) {
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL(
+          "/pdf/accessibility/paragraphs-and-heading-untagged.pdf")));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  ChromeTranslateClient* client = GetChromeTranslateClient();
+  ASSERT_TRUE(client);
+
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return client->GetLanguageState().pdf_translatability_status() !=
+           LanguageState::PdfTranslatabilityStatus::kNotChecked;
+  }));
+
+  EXPECT_EQ(client->GetLanguageState().pdf_translatability_status(),
+            LanguageState::PdfTranslatabilityStatus::kTranslatable);
+
+  auto* coordinator = PDFTranslationCoordinator::GetForCurrentDocument(
+      web_contents->GetPrimaryMainFrame());
+  ASSERT_TRUE(coordinator);
+  EXPECT_EQ(coordinator->status(),
+            PDFTranslationCoordinator::TranslatabilityStatus::kTranslatable);
+}
+
+// Test that loading an HTML page with an embedded PDF drops PDF translation
+// processing (it is not a full-page PDF), so PDF text is never sent to the main
+// frame for language detection and PDF translatability is never checked.
+IN_PROC_BROWSER_TEST_F(TranslateManagerPdfBrowserTest,
+                       EmbeddedPdfDroppedForTranslation) {
+  ChromeTranslateClient* client = GetChromeTranslateClient();
+  ASSERT_TRUE(client);
+
+  struct : TranslateDriver::LanguageDetectionObserver {
+    void OnLanguageDetermined(
+        const LanguageDetectionDetails& details) override {
+      last_contents = details.contents;
+    }
+    std::u16string last_contents;
+  } observer;
+  base::ScopedObservation<TranslateDriver,
+                          TranslateDriver::LanguageDetectionObserver>
+      observation(&observer);
+  observation.Observe(client->GetTranslateDriver());
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/pdf/pdf_embed.html")));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  pdf::PDFDocumentHelper* pdf_helper = nullptr;
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    pdf_helper = pdf::PDFDocumentHelper::MaybeGetForWebContents(*web_contents);
+    return pdf_helper != nullptr;
+  }));
+
+  base::test::TestFuture<void> document_loaded;
+  pdf_helper->RegisterForDocumentLoadComplete(document_loaded.GetCallback());
+  ASSERT_TRUE(document_loaded.Wait());
+
+  // Flush the PDF and renderer Mojo pipes to ensure any PdfPageCaptured() IPC
+  // triggered by OnDocumentLoadComplete() would have completed.
+  base::test::TestFuture<const std::u16string&> page_text;
+  pdf_helper->GetPageText(0, page_text.GetCallback());
+  ASSERT_FALSE(page_text.Get().empty());
+  ASSERT_TRUE(content::ExecJs(web_contents->GetPrimaryMainFrame(), ""));
+
+  EXPECT_NE(observer.last_contents, page_text.Get());
+  EXPECT_EQ(client->GetLanguageState().pdf_translatability_status(),
+            LanguageState::PdfTranslatabilityStatus::kNotChecked);
+  EXPECT_FALSE(PDFTranslationCoordinator::GetForCurrentDocument(
+      web_contents->GetPrimaryMainFrame()));
+}
+#endif  // BUILDFLAG(ENABLE_PDF)
+
 }  // namespace
 }  // namespace translate
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.