Chrome · WebUI
CVE-2026-87498
Logic Error in WebUI
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc |
modified |
Files Changed
chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.ccchrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.hchrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
Patch
From a95d8fc58b4624dc999a9ac9d7079ed2ec048541 Mon Sep 17 00:00:00 2001 From: Lauren Winston <[email protected]> Date: Fri, 04 Sep 2026 15:12:59 -0700 Subject: [PATCH] [M152] Add missing user activation checks to the UntrustedPageHandler. Original change's description: > Add missing user activation checks to the UntrustedPageHandler. > > This prevents Mojo commands from being executed (e.g. > OnLinkClicked) via a script or something other than > user action. > > Fixed:552413517 > > Change-Id: I9fac4ea6c74b4454d5fe6b2c92ddfbb0477287a9 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8282642 > Commit-Queue: Lauren Winston <[email protected]> > Reviewed-by: Kristi Saney <[email protected]> > Cr-Commit-Position: refs/heads/main@{#1686136} (cherry picked from commit 06262fce88f70dc902d29f6be3dbb8fb140a9432) Bug: 553316283,552413517 Change-Id: I9fac4ea6c74b4454d5fe6b2c92ddfbb0477287a9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8348823 Reviewed-by: Kristi Saney <[email protected]> Commit-Queue: Lauren Winston <[email protected]> Cr-Commit-Position: refs/branch-heads/7977@{#2439} Cr-Branched-From: b7fe14017379ddffae396d944fb8b59a5896c261-refs/heads/main@{#1669021} --- 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 59a9712..61322047 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 @@ -812,6 +812,11 @@ } void ReadAnythingUntrustedPageHandler::OnCopy() { + if (!HasTransientUserActivation()) { + VLOG(1) << "OnCopy failed with no transient user activation"; + return; + } + if (main_observer_ && main_observer_->web_contents()) { main_observer_->web_contents()->Copy(); } @@ -989,6 +994,11 @@ void ReadAnythingUntrustedPageHandler::OnLinkClicked( const ui::AXTreeID& target_tree_id, ui::AXNodeID target_node_id) { + if (!HasTransientUserActivation()) { + VLOG(1) << "OnLinkClicked failed with no transient user activation"; + return; + } + bool is_observing_tree = IsObservingTree(target_tree_id); base::UmaHistogramBoolean(kRendererLinkRequestHistogram, is_observing_tree); if (!is_observing_tree) { @@ -1167,6 +1177,11 @@ int anchor_offset, ui::AXNodeID focus_node_id, int focus_offset) { + if (!HasTransientUserActivation()) { + VLOG(1) << "OnSelectionChange failed with no transient user activation"; + return; + } + bool is_observing_tree = IsObservingTree(target_tree_id); base::UmaHistogramBoolean(kRendererSelectionRequestHistogram, is_observing_tree); @@ -1197,6 +1212,11 @@ } void ReadAnythingUntrustedPageHandler::OnCollapseSelection() { + if (!HasTransientUserActivation()) { + VLOG(1) << "OnCollapseSelection failed with no transient user activation"; + return; + } + if (main_observer_ && main_observer_->web_contents()) { main_observer_->web_contents()->CollapseSelection(); } @@ -1885,3 +1905,8 @@ } } #endif + +bool ReadAnythingUntrustedPageHandler::HasTransientUserActivation() const { + return web_ui_ && web_ui_->GetRenderFrameHost() && + web_ui_->GetRenderFrameHost()->HasTransientUserActivation(); +} diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h index e0b1ba585..2a15b62 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h @@ -366,6 +366,8 @@ content::WebContents* GetWebContents() const; + bool HasTransientUserActivation() const; + void OnScreenAIServiceInitialized(bool successful); // Called to notify this instance that the dependency parser loader diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc index 0016d2c..3094dfe 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc @@ -41,6 +41,7 @@ #include "components/prefs/pref_value_map.h" #include "components/tabs/public/tab_interface.h" #include "components/translate/core/browser/translate_manager.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" @@ -60,6 +61,9 @@ #include "ui/accessibility/mojom/ax_tree_update.mojom.h" #include "ui/accessibility/platform/browser_accessibility.h" #include "ui/accessibility/platform/browser_accessibility_manager.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/base/clipboard/clipboard_sequence_number_token.h" #include "ui/gfx/geometry/size.h" #if BUILDFLAG(IS_CHROMEOS) #include "base/test/bind.h" @@ -184,23 +188,25 @@ #if BUILDFLAG(IS_CHROMEOS) explicit TestReadAnythingUntrustedPageHandler( mojo::PendingRemote<read_anything::mojom::UntrustedPage> page, + mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler> + receiver, content::WebUI* test_web_ui, std::unique_ptr<ChromeOsExtensionWrapper> extension_wrapper) - : ReadAnythingUntrustedPageHandler( - std::move(page), - mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>(), - test_web_ui, - /*use_screen_ai_service=*/false, - std::move(extension_wrapper)) {} + : ReadAnythingUntrustedPageHandler(std::move(page), + std::move(receiver), + test_web_ui, + /*use_screen_ai_service=*/false, + std::move(extension_wrapper)) {} #else explicit TestReadAnythingUntrustedPageHandler( mojo::PendingRemote<read_anything::mojom::UntrustedPage> page, + mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler> + receiver, content::WebUI* test_web_ui) - : ReadAnythingUntrustedPageHandler( - std::move(page), - mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>(), - test_web_ui, - /*use_screen_ai_service=*/false) {} + : ReadAnythingUntrustedPageHandler(std::move(page), + std::move(receiver), + test_web_ui, + /*use_screen_ai_service=*/false) {} #endif }; @@ -315,6 +321,7 @@ #if BUILDFLAG(IS_CHROMEOS) extension_wrapper_ptr_ = nullptr; #endif + handler_remote_.reset(); handler_.reset(); test_web_ui_.reset(); web_contents_.reset(); @@ -322,17 +329,19 @@ } std::unique_ptr<TestReadAnythingUntrustedPageHandler> CreateHandler() { + handler_remote_.reset(); #if BUILDFLAG(IS_CHROMEOS) std::unique_ptr<ChromeOsExtensionWrapper> extension_wrapper_mock = std::make_unique<testing::NiceMock<MockChromeOsExtensionWrapper>>(); extension_wrapper_ptr_ = static_cast<MockChromeOsExtensionWrapper*>( extension_wrapper_mock.get()); return std::make_unique<TestReadAnythingUntrustedPageHandler>( - page_.BindAndGetRemote(), test_web_ui_.get(), - std::move(extension_wrapper_mock)); + page_.BindAndGetRemote(), handler_remote_.BindNewPipeAndPassReceiver(), + test_web_ui_.get(), std::move(extension_wrapper_mock)); #else return std::make_unique<TestReadAnythingUntrustedPageHandler>( - page_.BindAndGetRemote(), test_web_ui_.get()); + page_.BindAndGetRemote(), handler_remote_.BindNewPipeAndPassReceiver(), + test_web_ui_.get()); #endif } @@ -519,6 +528,7 @@ #endif testing::NiceMock<MockPage> page_; FakeTtsEngineDelegate engine_delegate_; + mojo::Remote<read_anything::mojom::UntrustedPageHandler> handler_remote_;
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
index 0016d2c..3094dfe 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
@@ -41,6 +41,7 @@
#include "components/prefs/pref_value_map.h"
#include "components/tabs/public/tab_interface.h"
#include "components/translate/core/browser/translate_manager.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
@@ -60,6 +61,9 @@
#include "ui/accessibility/mojom/ax_tree_update.mojom.h"
#include "ui/accessibility/platform/browser_accessibility.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/base/clipboard/clipboard_sequence_number_token.h"
#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/test/bind.h"
@@ -184,23 +188,25 @@
#if BUILDFLAG(IS_CHROMEOS)
explicit TestReadAnythingUntrustedPageHandler(
mojo::PendingRemote<read_anything::mojom::UntrustedPage> page,
+ mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>
+ receiver,
content::WebUI* test_web_ui,
std::unique_ptr<ChromeOsExtensionWrapper> extension_wrapper)
- : ReadAnythingUntrustedPageHandler(
- std::move(page),
- mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>(),
- test_web_ui,
- /*use_screen_ai_service=*/false,
- std::move(extension_wrapper)) {}
+ : ReadAnythingUntrustedPageHandler(std::move(page),
+ std::move(receiver),
+ test_web_ui,
+ /*use_screen_ai_service=*/false,
+ std::move(extension_wrapper)) {}
#else
explicit TestReadAnythingUntrustedPageHandler(
mojo::PendingRemote<read_anything::mojom::UntrustedPage> page,
+ mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>
+ receiver,
content::WebUI* test_web_ui)
- : ReadAnythingUntrustedPageHandler(
- std::move(page),
- mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>(),
- test_web_ui,
- /*use_screen_ai_service=*/false) {}
+ : ReadAnythingUntrustedPageHandler(std::move(page),
+ std::move(receiver),
+ test_web_ui,
+ /*use_screen_ai_service=*/false) {}
#endif
};
@@ -315,6 +321,7 @@
#if BUILDFLAG(IS_CHROMEOS)
extension_wrapper_ptr_ = nullptr;
#endif
+ handler_remote_.reset();
handler_.reset();
test_web_ui_.reset();
web_contents_.reset();
@@ -322,17 +329,19 @@
}
std::unique_ptr<TestReadAnythingUntrustedPageHandler> CreateHandler() {
+ handler_remote_.reset();
#if BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<ChromeOsExtensionWrapper> extension_wrapper_mock =
std::make_unique<testing::NiceMock<MockChromeOsExtensionWrapper>>();
extension_wrapper_ptr_ = static_cast<MockChromeOsExtensionWrapper*>(
extension_wrapper_mock.get());
return std::make_unique<TestReadAnythingUntrustedPageHandler>(
- page_.BindAndGetRemote(), test_web_ui_.get(),
- std::move(extension_wrapper_mock));
+ page_.BindAndGetRemote(), handler_remote_.BindNewPipeAndPassReceiver(),
+ test_web_ui_.get(), std::move(extension_wrapper_mock));
#else
return std::make_unique<TestReadAnythingUntrustedPageHandler>(
- page_.BindAndGetRemote(), test_web_ui_.get());
+ page_.BindAndGetRemote(), handler_remote_.BindNewPipeAndPassReceiver(),
+ test_web_ui_.get());
#endif
}
@@ -519,6 +528,7 @@
#endif
testing::NiceMock<MockPage> page_;
FakeTtsEngineDelegate engine_delegate_;
+ mojo::Remote<read_anything::mojom::UntrustedPageHandler> handler_remote_;
std::unique_ptr<ReadAnythingUntrustedPageHandler> handler_;
std::unique_ptr<content::WebContents> web_contents_;
std::unique_ptr<content::TestWebUI> test_web_ui_;
@@ -1497,8 +1507,8 @@
EXPECT_CALL(*extension_wrapper_ptr_, ActivateSpeechEngine).Times(1);
handler_ = std::make_unique<TestReadAnythingUntrustedPageHandler>(
- page_.BindAndGetRemote(), test_web_ui_.get(),
- std::move(extension_wrapper_mock));
+ page_.BindAndGetRemote(), handler_remote_.BindNewPipeAndPassReceiver(),
+ test_web_ui_.get(), std::move(extension_wrapper_mock));
}
IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
@@ -2397,6 +2407,189 @@
EXPECT_FALSE(handler_->dom_distiller_content().has_value());
}
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+ OnLinkClicked_RequiresTransientUserActivation) {
+ const std::string histogram_name =
+ "Accessibility.ReadAnything.RendererRequestForLinkClick."
+ "IsFromObservedTree";
+ base::HistogramTester histogram_tester;
+
+ ASSERT_TRUE(
+ content::NavigateToURL(web_contents_.get(), GURL(url::kAboutBlankURL)));
+ test_web_ui_->set_render_frame_host(web_contents_->GetPrimaryMainFrame());
+ handler_ = CreateHandler();
+
+ content::RenderFrameHost* rfh = web_contents_->GetPrimaryMainFrame();
+ content::RenderFrameHost* main_rfh =
+ browser()->GetActiveTabInterface()->GetContents()->GetPrimaryMainFrame();
+ ui::AXTreeID tree_id = main_rfh->GetAXTreeID();
+ ui::AXNodeID node_id = 1;
+
+ // Initial state with no transient user activation
+ EXPECT_FALSE(rfh->HasTransientUserActivation());
+ handler_remote_->OnLinkClicked(tree_id, node_id);
+ handler_remote_.FlushForTesting();
+ histogram_tester.ExpectTotalCount(histogram_name, 0);
+
+ // Grant User Activation via simulated mouse click.
+ content::SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents_.get());
+ content::SimulateMouseClick(web_contents_.get(), 0,
+ blink::WebMouseEvent::Button::kLeft);
+ ASSERT_TRUE(base::test::RunUntil(
+ [&]() { return rfh->HasTransientUserActivation(); }));
+ EXPECT_TRUE(rfh->HasTransientUserActivation());
+ handler_remote_->OnLinkClicked(tree_id, node_id);
+ handler_remote_.FlushForTesting();
+ histogram_tester.ExpectUniqueSample(histogram_name, /*sample=*/true,
+ /*expected_bucket_count=*/1);
+}
+
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+ OnSelectionChange_RequiresTransientUserActivation) {
+ const std::string histogram_name =
+ "Accessibility.ReadAnything.RendererRequestForSelection."
+ "IsFromObservedTree";
+ base::HistogramTester histogram_tester;
+
+ ASSERT_TRUE(
+ content::NavigateToURL(web_contents_.get(), GURL(url::kAboutBlankURL)));
+ test_web_ui_->set_render_frame_host(web_contents_->GetPrimaryMainFrame());
+ handler_ = CreateHandler();
+
+ content::RenderFrameHost* rfh = web_contents_->GetPrimaryMainFrame();
+ content::RenderFrameHost* main_rfh =
+ browser()->GetActiveTabInterface()->GetContents()->GetPrimaryMainFrame();
+ ui::AXTreeID tree_id = main_rfh->GetAXTreeID();
+
+ // Initial state with no transient user activation
+ EXPECT_FALSE(rfh->HasTransientUserActivation());
+ handler_remote_->OnSelectionChange(tree_id, /*anchor_node_id=*/1, 0,
+ /*focus_node_id=*/2, 5);
+ handler_remote_.FlushForTesting();
+ histogram_tester.ExpectTotalCount(histogram_name, 0);
+
+ // Grant User Activation via simulated mouse click.
+ content::SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents_.get());
+ content::SimulateMouseClick(web_contents_.get(), 0,
+ blink::WebMouseEvent::Button::kLeft);
+ ASSERT_TRUE(base::test::RunUntil(
+ [&]() { return rfh->HasTransientUserActivation(); }));
+ EXPECT_TRUE(rfh->HasTransientUserActivation());
+ handler_remote_->OnSelectionChange(tree_id, /*anchor_node_id=*/1, 0,
+ /*focus_node_id=*/2, 5);
+ handler_remote_.FlushForTesting();
+ histogram_tester.ExpectUniqueSample(histogram_name, /*sample=*/true,
+ /*expected_bucket_count=*/1);
+}
+
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+ NullRenderFrameHost_ReturnsSafely) {
+ test_web_ui_->set_render_frame_host(nullptr);
+ handler_ = CreateHandler();
+
+ content::RenderFrameHost* main_rfh =
+ browser()->GetActiveTabInterface()->GetContents()->GetPrimaryMainFrame();
+ ui::AXTreeID tree_id = main_rfh->GetAXTreeID();
+
+ handler_remote_->OnCopy();
+ handler_remote_->OnLinkClicked(tree_id, 1);
+ handler_remote_->OnSelectionChange(tree_id, 1, 0, 1, 1);
+ handler_remote_->OnCollapseSelection();
+ handler_remote_.FlushForTesting();
+}
+
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+ OnCopy_RequiresTransientUserActivation) {
+ content::WebContents* main_contents =
+ browser()->GetActiveTabInterface()->GetContents();
+ ASSERT_TRUE(content::NavigateToURL(
+ main_contents, GURL("data:text/html,<div>Hello World</div>")));
+ main_contents->Focus();
+ main_contents->SelectAll();
+
+ ASSERT_TRUE(base::test::RunUntil([&]() {
+ content::RenderWidgetHostView* view =
+ main_contents->GetRenderWidgetHostView();
+ return view && !view->GetSelectedText().empty();
+ }));
+
+ ASSERT_TRUE(
+ content::NavigateToURL(web_contents_.get(), GURL(url::kAboutBlankURL)));
+ test_web_ui_->set_render_frame_host(web_contents_->GetPrimaryMainFrame());
+ handler_ = CreateHandler();
+
+ content::RenderFrameHost* rfh = web_contents_->GetPrimaryMainFrame();
+ ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+ const ui::ClipboardSequenceNumberToken initial_seq =
+ clipboard->GetSequenceNumber(ui::ClipboardBuffer::kCopyPaste);
+
+ // Initial state with no transient user activation
+ EXPECT_FALSE(rfh->HasTransientUserActivation());
+ handler_remote_->OnCopy();
+ handler_remote_.FlushForTesting();
+ EXPECT_EQ(clipboard->GetSequenceNumber(ui::ClipboardBuffer::kCopyPaste),
+ initial_seq);
+
+ // Grant User Activation via simulated mouse click.
+ content::SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents_.get());
+ content::SimulateMouseClick(web_contents_.get(), 0,
+ blink::WebMouseEvent::Button::kLeft);
+ ASSERT_TRUE(base::test::RunUntil(
+ [&]() { return rfh->HasTransientUserActivation(); }));
+
+ handler_remote_->OnCopy();
+ handler_remote_.FlushForTesting();
+ ASSERT_TRUE(base::test::RunUntil([&]() {
+ return clipboard->GetSequenceNumber(ui::ClipboardBuffer::kCopyPaste) !=
+ initial_seq;
+ }));
+}
+
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+ OnCollapseSelection_RequiresTransientUserActivation) {
+ content::WebContents* main_contents =
+ browser()->GetActiveTabInterface()->GetContents();
+ ASSERT_TRUE(content::NavigateToURL(
+ main_contents, GURL("data:text/html,<div>Selected Text</div>")));
+ main_contents->Focus();
+ main_contents->SelectAll();
+
+ ASSERT_TRUE(base::test::RunUntil([&]() {
+ content::RenderWidgetHostView* view =
+ main_contents->GetRenderWidgetHostView();
+ return view && !view->GetSelectedText().empty();
+ }));
+
+ ASSERT_TRUE(
+ content::NavigateToURL(web_contents_.get(), GURL(url::kAboutBlankURL)));
+ test_web_ui_->set_render_frame_host(web_contents_->GetPrimaryMainFrame());
+ handler_ = CreateHandler();
+
+ content::RenderFrameHost* rfh = web_contents_->GetPrimaryMainFrame();
+
+ // Initial state with no transient user activation
+ EXPECT_FALSE(rfh->HasTransientUserActivation());
+ handler_remote_->OnCollapseSelection();
+ handler_remote_.FlushForTesting();
+ EXPECT_FALSE(
+ main_contents->GetRenderWidgetHostView()->GetSelectedText().empty());
+
+ // Grant User Activation via simulated mouse click.
+ content::SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents_.get());
+ content::SimulateMouseClick(web_contents_.get(), 0,
+ blink::WebMouseEvent::Button::kLeft);
+ ASSERT_TRUE(base::test::RunUntil(
+ [&]() { return rfh->HasTransientUserActivation(); }));
+
+ handler_remote_->OnCollapseSelection();
+ handler_remote_.FlushForTesting();
+ ASSERT_TRUE(base::test::RunUntil([&]() {
+ content::RenderWidgetHostView* view =
+ main_contents->GetRenderWidgetHostView();
+ return view && view->GetSelectedText().empty();
+ }));
+}
+
} // namespace
INSTANTIATE_TEST_SUITE_P(All,
ReadAnythingUntrustedPageHandlerTest,
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