Chrome · StorageAccessAPI
CVE-2026-78896
Logic Error in StorageAccessAPI
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
forchrome/browser/storage_access_api/api_browsertest.cc |
modified | |
IN_PROC_BROWSER_TEST_Fchrome/browser/storage_access_api/api_browsertest.cc |
modified | |
ifchrome/browser/storage_access_api/storage_access_grant_permission_context.cc |
modified | |
ifchrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc |
modified |
Files Changed
chrome/browser/storage_access_api/api_browsertest.ccchrome/browser/storage_access_api/storage_access_grant_permission_context.ccchrome/browser/storage_access_api/storage_access_grant_permission_context.hchrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
Patch
From 1b717d86dbb44d6227a6f693931c91157624460e Mon Sep 17 00:00:00 2001
From: Chris Fredrickson <cfredric@chromium.org>
Date: Wed, 01 Jul 2026 09:20:01 -0700
Subject: [PATCH] [SAA] Fix embargoed permissions exposure
This prevents exposure of an automatic denial via permissions embargo,
per the SAA specification. (Note: this does not disable the embargo,
meaning the user will not be prompted >3 times if they continually
dismiss the prompt.)
Fixed: 519229463
Change-Id: I60985c89171297770a169041c7eea60258986b31
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7909441
Reviewed-by: Antonio Sartori <antoniosartori@chromium.org>
Auto-Submit: Chris Fredrickson <cfredric@chromium.org>
Reviewed-by: Chris Fredrickson <cfredric@chromium.org>
Commit-Queue: Chris Fredrickson <cfredric@chromium.org>
Reviewed-by: Johann Hofmann <johannhof@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1655543}
---
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index 6f3da6a..9ed824a 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -918,6 +918,37 @@
UnorderedElementsAre(Pair(net::SchemefulSite(GURL(kOriginB)), false)));
}
+// Test that permissions.query inside a credentialless iframe does not expose
+// embargoed status when a storage access request has been repeatedly dismissed.
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest,
+ PermissionQueryEmbargoed_CredentiallessFrame) {
+ SetBlockThirdPartyCookies(true);
+ EnsureUserInteractionOn(kHostB);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/echoheader?cookie");
+
+ prompt_factory()->set_response_type(
+ permissions::PermissionRequestManager::DISMISS);
+
+ // Dismissing the prompt 3 times places the origin under embargo.
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_FALSE(
+ content::ExecJs(GetFrame(), "document.requestStorageAccess()"));
+ }
+ ASSERT_EQ(prompt_factory()->TotalRequestCount(), 3);
+ ASSERT_EQ(QueryPermission(GetFrame()), "prompt");
+
+ NavigateToPageWithFrame(kHostA, /*browser_ptr=*/nullptr,
+ /*credentialless=*/true);
+ NavigateFrameTo(kHostB, "/echoheader?cookie");
+ EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame()));
+
+ // Even when under embargo, permissions.query inside a credentialless iframe
+ // should return "prompt" rather than "denied".
+ EXPECT_EQ(QueryPermission(GetFrame()), "prompt");
+}
+
IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryCrossSite) {
SetBlockThirdPartyCookies(true);
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
index 881a364..18b4fdce 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
@@ -407,29 +407,6 @@
return;
}
- {
- // Normally a previous prompt rejection would already be filtered before
- // reaching `StorageAccessGrantPermissionContext::DecidePermission`, but the
- // requirement not to surface the user's denial back to the caller means
- // this code is reachable even after permission has been blocked.
- // Accordingly, check the default implementation, and if a denial has been
- // persisted, respect that decision.
- ContentSetting existing_setting = permissions::
- ContentSettingPermissionContextBase::GetContentSettingStatusInternal(
- rfh, request_data->requesting_origin,
- request_data->embedding_origin);
- // ALLOW grants are handled by ContentSettingPermissionContextBase so they
- // never reach this point.
- CHECK_NE(existing_setting, CONTENT_SETTING_ALLOW);
- if (existing_setting == CONTENT_SETTING_BLOCK) {
- NotifyPermissionSetInternal(*request_data, std::move(callback),
- /*persist=*/false, PermissionDecision::kDeny,
- RequestOutcome::kReusedPreviousDecision);
- return;
- }
- CHECK_EQ(existing_setting, CONTENT_SETTING_ASK);
- }
-
// FedCM grants (and the appropriate permissions policy) may allow the call to
// auto-resolve (without granting a new permission).
if (FederatedIdentityPermissionContext* fedcm_context =
@@ -582,7 +559,7 @@
const GURL& embedding_origin) const {
if (render_frame_host) {
if (IsAccessRestrictedInFrame(render_frame_host)) {
- return CONTENT_SETTING_ASK;
+ return CONTENT_SETTING_BLOCK;
}
// Permission query from top-level frame should be "granted" by default.
@@ -591,16 +568,9 @@
}
}
- ContentSetting setting = permissions::ContentSettingPermissionContextBase::
+ return permissions::ContentSettingPermissionContextBase::
GetContentSettingStatusInternal(render_frame_host, requesting_origin,
embedding_origin);
-
- // The spec calls for avoiding exposure of rejections to prevent any attempt
- // at retaliating against users who would reject a prompt.
- if (setting == CONTENT_SETTING_BLOCK) {
- return CONTENT_SETTING_ASK;
- }
- return setting;
}
void StorageAccessGrantPermissionContext::NotifyPermissionSet(
@@ -641,6 +611,15 @@
decision.overall_decision, outcome);
}
+void StorageAccessGrantPermissionContext::MaybeOverridePermissionResultToReturn(
+ content::PermissionResult& result) const {
+ // The spec calls for avoiding exposure of rejections to prevent any attempt
+ // at retaliating against users who would reject a prompt.
+ if (result.status == content::PermissionStatus::DENIED) {
+ result.status = content::PermissionStatus::ASK;
+ }
+}
+
void StorageAccessGrantPermissionContext::ReportRelatedWebsiteSetsDeprecation(
content::RenderFrameHost* rfh) {
auto deprecation_details = blink::mojom::DeprecationIssueDetails::New();
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.h b/chrome/browser/storage_access_api/storage_access_grant_permission_context.h
index 1d2e4ed9..19254db 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.h
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.h
@@ -91,6 +91,10 @@
std::unique_ptr<permissions::PermissionRequestData> request_data,
permissions::BrowserPermissionCallback callback);
+ // PermissionContextBase:
+ void MaybeOverridePermissionResultToReturn(
+ content::PermissionResult& result) const override;
+
static int GetImplicitGrantLimitForTesting();
static void SetImplicitGrantLimitForTesting(int limit);
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index ff068b66..6c3e083a 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -43,6 +43,7 @@
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "content/public/browser/btm_service.h"
+#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/web_contents.h"
@@ -85,6 +86,7 @@
constexpr char kPromptResultHistogram[] = "Permissions.Action.StorageAccess";
constexpr char kRequestOutcomeHistogram[] = "API.StorageAccess.RequestOutcome";
constexpr int kImplicitGrantLimit = 5; // Implicit grant limit for testing.
+constexpr int kDefaultDismissalsBeforeEmbargo = 3;
const uint64_t kDwaEventNameHash =
base::HashMetricName("StorageAccess.RequestOutcome");
@@ -196,12 +198,20 @@
ChromeRenderViewHostTestHarness::TearDown();
}
+ content::RenderFrameHost* RenderFrameHostFromID(
+ content::GlobalRenderFrameHostId id) {
+ content::RenderFrameHost* target_rfh = content::RenderFrameHost::FromID(id);
+ return target_rfh ? target_rfh : main_rfh();
+ }
+
base::test::TestFuture<content::PermissionResult> DecidePermission(
std::unique_ptr<permissions::PermissionRequestData> request_data,
bool simulate_user_gesture = true) {
DCHECK(request_data);
if (request_data->user_gesture && simulate_user_gesture) {
- content::RenderFrameHostTester::For(main_rfh())->SimulateUserActivation();
+ content::RenderFrameHostTester::For(
+ RenderFrameHostFromID(request_data->id.global_render_frame_host_id()))
+ ->SimulateUserActivation();
}
base::test::TestFuture<content::PermissionResult> future;
permission_context_->DecidePermissionForTesting(std::move(request_data),
@@ -213,7 +223,9 @@
std::unique_ptr<permissions::PermissionRequestData> request_data) {
DCHECK(request_data);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index 6f3da6a..9ed824a 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -918,6 +918,37 @@
UnorderedElementsAre(Pair(net::SchemefulSite(GURL(kOriginB)), false)));
}
+// Test that permissions.query inside a credentialless iframe does not expose
+// embargoed status when a storage access request has been repeatedly dismissed.
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest,
+ PermissionQueryEmbargoed_CredentiallessFrame) {
+ SetBlockThirdPartyCookies(true);
+ EnsureUserInteractionOn(kHostB);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/echoheader?cookie");
+
+ prompt_factory()->set_response_type(
+ permissions::PermissionRequestManager::DISMISS);
+
+ // Dismissing the prompt 3 times places the origin under embargo.
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_FALSE(
+ content::ExecJs(GetFrame(), "document.requestStorageAccess()"));
+ }
+ ASSERT_EQ(prompt_factory()->TotalRequestCount(), 3);
+ ASSERT_EQ(QueryPermission(GetFrame()), "prompt");
+
+ NavigateToPageWithFrame(kHostA, /*browser_ptr=*/nullptr,
+ /*credentialless=*/true);
+ NavigateFrameTo(kHostB, "/echoheader?cookie");
+ EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame()));
+
+ // Even when under embargo, permissions.query inside a credentialless iframe
+ // should return "prompt" rather than "denied".
+ EXPECT_EQ(QueryPermission(GetFrame()), "prompt");
+}
+
IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryCrossSite) {
SetBlockThirdPartyCookies(true);
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index ff068b66..6c3e083a 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -43,6 +43,7 @@
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "content/public/browser/btm_service.h"
+#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/web_contents.h"
@@ -85,6 +86,7 @@
constexpr char kPromptResultHistogram[] = "Permissions.Action.StorageAccess";
constexpr char kRequestOutcomeHistogram[] = "API.StorageAccess.RequestOutcome";
constexpr int kImplicitGrantLimit = 5; // Implicit grant limit for testing.
+constexpr int kDefaultDismissalsBeforeEmbargo = 3;
const uint64_t kDwaEventNameHash =
base::HashMetricName("StorageAccess.RequestOutcome");
@@ -196,12 +198,20 @@
ChromeRenderViewHostTestHarness::TearDown();
}
+ content::RenderFrameHost* RenderFrameHostFromID(
+ content::GlobalRenderFrameHostId id) {
+ content::RenderFrameHost* target_rfh = content::RenderFrameHost::FromID(id);
+ return target_rfh ? target_rfh : main_rfh();
+ }
+
base::test::TestFuture<content::PermissionResult> DecidePermission(
std::unique_ptr<permissions::PermissionRequestData> request_data,
bool simulate_user_gesture = true) {
DCHECK(request_data);
if (request_data->user_gesture && simulate_user_gesture) {
- content::RenderFrameHostTester::For(main_rfh())->SimulateUserActivation();
+ content::RenderFrameHostTester::For(
+ RenderFrameHostFromID(request_data->id.global_render_frame_host_id()))
+ ->SimulateUserActivation();
}
base::test::TestFuture<content::PermissionResult> future;
permission_context_->DecidePermissionForTesting(std::move(request_data),
@@ -213,7 +223,9 @@
std::unique_ptr<permissions::PermissionRequestData> request_data) {
DCHECK(request_data);
if (request_data->user_gesture) {
- content::RenderFrameHostTester::For(main_rfh())->SimulateUserActivation();
+ content::RenderFrameHostTester::For(
+ RenderFrameHostFromID(request_data->id.global_render_frame_host_id()))
+ ->SimulateUserActivation();
}
base::test::TestFuture<content::PermissionResult> future;
permission_context_->RequestPermissionForTesting(std::move(request_data),
@@ -222,14 +234,15 @@
}
std::unique_ptr<permissions::PermissionRequestData> MakePermissionRequestData(
- bool user_gesture) {
+ bool user_gesture,
+ content::RenderFrameHost* rfh = nullptr) {
return std::make_unique<permissions::PermissionRequestData>(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
permissions::PermissionUtil::
ContentSettingsTypeToPermissionType(
ContentSettingsType::STORAGE_ACCESS)),
- CreateFakeID(), user_gesture, GetRequesterURL(), GetTopLevelURL());
+ CreateFakeID(rfh), user_gesture, GetRequesterURL(), GetTopLevelURL());
}
// Helper to ensure that a given content setting is consistently applied on a
@@ -417,17 +430,14 @@
// Querying permission from a credentialless frame should return ASK (prompt)
// even if there is a grant.
- EXPECT_EQ(
- PermissionStatus::ASK,
- permission_context()
- ->GetPermissionStatus(
- content::PermissionDescriptorUtil::
- CreatePermissionDescriptorForPermissionType(
- permissions::PermissionUtil::
- ContentSettingsTypeToPermissionType(
- permission_context()->content_settings_type())),
- child_rfh, GetRequesterURL(), GetTopLevelURL())
- .status);
+ content::PermissionResult result = permission_context()->GetPermissionStatus(
+ content::PermissionDescriptorUtil::
+ CreatePermissionDescriptorForPermissionType(
+ permissions::PermissionUtil::ContentSettingsTypeToPermissionType(
+ permission_context()->content_settings_type())),
+ child_rfh, GetRequesterURL(), GetTopLevelURL());
+ permission_context()->MaybeOverridePermissionResultToReturn(result);
+ EXPECT_EQ(PermissionStatus::ASK, result.status);
}
// Test that after a successful explicit storage access grant, there's a content
@@ -897,6 +907,14 @@
TEST_F(StorageAccessGrantPermissionContextTest,
ExplicitGrantDenialNotExposedViaQuery) {
+ content::RenderFrameHost* child_rfh =
+ content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
+ std::unique_ptr<content::NavigationSimulator> navigation =
+ content::NavigationSimulator::CreateRendererInitiated(GetRequesterURL(),
+ child_rfh);
+ navigation->Commit();
+ child_rfh = navigation->GetFinalRenderFrameHost();
+
// Set the content setting to blocked, mimicking a prompt rejection by the
// user.
HostContentSettingsMap* settings_map =
@@ -908,26 +926,22 @@
prompt_factory().set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
- auto future =
- DecidePermission(MakePermissionRequestData(/*user_gesture=*/true));
+ auto future = RequestPermission(
+ MakePermissionRequestData(/*user_gesture=*/true, child_rfh));
// Ensure the prompt is not shown.
ASSERT_FALSE(request_manager()->IsRequestInProgress());
EXPECT_EQ(PermissionStatus::DENIED, future.Get().status);
// However, ensure that the user's denial is not exposed when querying the
// permission, per the spec.
- EXPECT_EQ(
- PermissionStatus::ASK,
- permission_context()
- ->GetPermissionStatus(
- content::PermissionDescriptorUtil::
- CreatePermissionDescriptorForPermissionType(
- permissions::PermissionUtil::
- ContentSettingsTypeToPermissionType(
- permission_context()->content_settings_type())),
- /*render_frame_host=*/nullptr, GetRequesterURL(),
- GetTopLevelURL())
- .status);
+ content::PermissionResult result = permission_context()->GetPermissionStatus(
+ content::PermissionDescriptorUtil::
+ CreatePermissionDescriptorForPermissionType(
+ permissions::PermissionUtil::ContentSettingsTypeToPermissionType(
+ permission_context()->content_settings_type())),
+ child_rfh, GetRequesterURL(), GetTopLevelURL());
+ permission_context()->MaybeOverridePermissionResultToReturn(result);
+ EXPECT_EQ(PermissionStatus::ASK, result.status);
EXPECT_THAT(page_specific_content_settings()->GetTwoSiteRequests(
ContentSettingsType::STORAGE_ACCESS),
@@ -1045,6 +1059,67 @@
IsEmpty());
}
+TEST_F(StorageAccessGrantPermissionContextTest, RepeatedDismissalsNotExposed) {
+ content::RenderFrameHost* child_rfh =
+ content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
+ std::unique_ptr<content::NavigationSimulator> navigation =
+ content::NavigationSimulator::CreateRendererInitiated(GetRequesterURL(),
+ child_rfh);
+ navigation->Commit();
+ child_rfh = navigation->GetFinalRenderFrameHost();
+
+ prompt_factory().set_response_type(
+ permissions::PermissionRequestManager::AutoResponseType::DISMISS);
+
+ for (int i = 0; i < kDefaultDismissalsBeforeEmbargo; ++i) {
+ EXPECT_EQ(PermissionStatus::ASK,
+ RequestPermission(
+ MakePermissionRequestData(/*user_gesture=*/true, child_rfh))
+ .Get()
+ .status);
+ }
+
+ content::PermissionResult result = permission_context()->GetPermissionStatus(
+ content::PermissionDescriptorUtil::
+ CreatePermissionDescriptorForPermissionType(
+ permissions::PermissionUtil::ContentSettingsTypeToPermissionType(
+ permission_context()->content_settings_type())),
+ child_rfh, GetRequesterURL(), GetTopLevelURL());
+ permission_context()->MaybeOverridePermissionResultToReturn(result);
+ EXPECT_EQ(PermissionStatus::ASK, result.status);
+}
+
+TEST_F(StorageAccessGrantPermissionContextTest,
+ EmbargoActivatesAfterRepeatedDismissals) {
+ content::RenderFrameHost* child_rfh =
+ content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
+ std::unique_ptr<content::NavigationSimulator> navigation =
+ content::NavigationSimulator::CreateRendererInitiated(GetRequesterURL(),
+ child_rfh);
+ navigation->Commit();
+ child_rfh = navigation->GetFinalRenderFrameHost();
+
+ prompt_factory().set_response_type(
+ permissions::PermissionRequestManager::AutoResponseType::DISMISS);
+
+ for (int i = 0; i < kDefaultDismissalsBeforeEmbargo; ++i) {
+ EXPECT_EQ(PermissionStatus::ASK,
+ RequestPermission(
+ MakePermissionRequestData(/*user_gesture=*/true, child_rfh))
+ .Get()
+ .status);
+ }
+
+ prompt_factory().set_response_type(
+ permissions::PermissionRequestManager::AutoResponseType::NONE);
+
+ EXPECT_EQ(PermissionStatus::DENIED,
+ RequestPermission(
+ MakePermissionRequestData(/*user_gesture=*/true, child_rfh))
+ .Get()
+ .status);
+}
+
class StorageAccessGrantPermissionContextAPIWithFedCMConnectionTest
: public StorageAccessGrantPermissionContextTest,
public testing::WithParamInterface<bool> {
diff --git a/components/permissions/permission_manager_unittest.cc b/components/permissions/permission_manager_unittest.cc
index 96c3f65..b215d147 100644
--- a/components/permissions/permission_manager_unittest.cc
+++ b/components/permissions/permission_manager_unittest.cc
@@ -938,6 +938,34 @@
EXPECT_TRUE(!context);
}
+TEST_F(PermissionManagerTest, StorageAccessPermissionStatusMasksDenied) {
+ SetPermission(PermissionType::STORAGE_ACCESS_GRANT,
+ PermissionStatus::GRANTED);
+ CheckPermissionStatus(PermissionType::STORAGE_ACCESS_GRANT,
+ PermissionStatus::GRANTED);
+
+ SetPermission(PermissionType::STORAGE_ACCESS_GRANT, PermissionStatus::ASK);
+ CheckPermissionStatus(PermissionType::STORAGE_ACCESS_GRANT,
+ PermissionStatus::ASK);
+
+ SetPermission(PermissionType::STORAGE_ACCESS_GRANT, PermissionStatus::DENIED);
+ CheckPermissionStatus(PermissionType::STORAGE_ACCESS_GRANT,
+ PermissionStatus::ASK);
+ CheckPermissionResult(PermissionType::STORAGE_ACCESS_GRANT,
+ PermissionStatus::ASK,
+ content::PermissionStatusSource::UNSPECIFIED);
+}
+
+TEST_F(PermissionManagerTest, StorageAccessPermissionRequestMasksDenied) {
+ NavigateAndCommit(url());
+ SetPermission(PermissionType::STORAGE_ACCESS_GRANT, PermissionStatus::DENIED);
+
+ RequestPermissionFromCurrentDocument(PermissionType::STORAGE_ACCESS_GRANT,
+ main_rfh());
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(PermissionStatus::ASK, callback_result());
+}
+
class PermissionManagerWithGeolocationTest : public PermissionManagerTest {
public:
PermissionManagerWithGeolocationTest() {
diff --git a/components/permissions/test/permission_test_util.cc b/components/permissions/test/permission_test_util.cc
index 3818979..4592373a 100644
--- a/components/permissions/test/permission_test_util.cc
... (truncated)
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