Low watch CVSS 6.5 chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Media
DescriptionIncorrect authorization in Media
ComponentMedia
Sub-componentMedia
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker520002854
Fix commit9a158bed51d4 (chromium/src) +161/-22
CWECWE-863
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-06-04 (per vendor advisory)
Fix landed2026-07-07 (public git)
Disclosed2026-08-25
Patch gap49 days — fix public in source before the advisory

Background

`AudibilityBypassTracker`
Per-document (DocumentUserData) tracker recording grants that let a media player bypass the browser’s audio service for audibility accounting, used by MediaFoundationRenderer.
`MediaFoundationRenderer`
A Windows media renderer running in a utility process that bypasses the browser’s audio service, whose creation authorizes the frame for an audibility bypass claim.
Audibility bypass grant
Authorization allowing a player to be counted as (in)audible without going through the audio service, which drives the tab audio indicator and power wake-locks.
`ScopedGrant` / `base::ScopedClosureRunner`
A move-only RAII holder that runs a revocation closure (RevokeGrant) when destroyed, tying a grant to an object’s lifetime.

Root Cause Analysis

Pre-patch, AudibilityBypassTracker::AddGrant() merely incremented a document-wide integer pending_grants_, and grants were fungible: CreateMediaFoundationRenderer called AddGrant() but never tied that grant to the renderer’s Mojo pipe lifetime. A compromised renderer could repeatedly invoke CreateMediaFoundationRenderer and immediately drop the pipes, minting an unbounded number of pending grants that persisted in the pool with no owner and no revocation. Any player could then ClaimGrant() from this pool to spoof the tab audio indicator and acquire power wake-locks without actually running an authorized MediaFoundationRenderer.

The fix makes AddGrant() return a move-only ScopedGrant (a base::ScopedClosureRunner bound to RevokeGrant(rfh_id, grant_id)), interposes a MediaFoundationRendererExtensionProxy that owns the ScopedGrant and is a self-owned receiver on the extension pipe, so if either the renderer or the utility process disconnects, the grant is revoked; internally the integer counter becomes a flat_set<int> pending_grants_ and a flat_map<MediaPlayerId,int> active_grants_ keyed by unique grant_id.

Key insight
The single mistake was decoupling a bypass grant’s lifetime from the MediaFoundationRenderer’s Mojo pipe—storing it as a fungible document-wide counter with no owner—so grants could be minted and abandoned indefinitely; the fix binds each grant to a ScopedGrant owned by a pipe-lifetime-observing proxy that revokes on disconnect.

Attack Path

  1. Compromise a renderer The attacker controls a renderer process able to issue CreateMediaFoundationRenderer requests to the browser’s MediaInterfaceProxy.
  2. Mint grants Repeatedly call CreateMediaFoundationRenderer; each call invokes AudibilityBypassTracker::AddGrant(), incrementing the document-wide pending_grants_ counter.
  3. Drop the pipes Immediately drop the renderer/extension Mojo pipes; pre-patch there was no disconnect handler, so the pending grants remained in the pool unrevoked.
  4. Claim spoofed grants Have arbitrary players call ClaimGrant() to consume pooled grants, spoofing the tab audio indicator and acquiring power wake-locks without an authorized MediaFoundationRenderer actually playing.

Impact Assessment

A compromised renderer gains incorrect authorization: it can mint arbitrarily many audibility bypass grants and use them to spoof the tab audio indicator and acquire power wake-locks, from the renderer/browser media pipeline on Windows (BUILDFLAG(IS_WIN)). Preconditions are a compromised renderer able to call CreateMediaFoundationRenderer. The advisory classifies this as incorrect authorization in Media with no exploited-in-the-wild status stated; no memory-safety escalation is claimed.

Changed Functions

FunctionChangeNotes
MediaFoundationRendererExtensionProxy
content/browser/media/media_interface_proxy.cc
modified
if
content/browser/media/media_interface_proxy.cc
modified
if
content/browser/media/media_web_contents_observer.cc
modified
TEST_F
content/browser/media/media_web_contents_observer_unittest.cc
modified

Files Changed

  • content/browser/media/media_web_contents_observer.cc
  • content/browser/media/media_interface_proxy.cc
  • content/browser/media/media_web_contents_observer_unittest.cc
  • content/browser/media/media_web_contents_observer.h

Audit Directions

  • Grant lifetime vs. pipe lifetime
    Audit authorization grants issued on Mojo interface creation that are not tied to that interface’s connection lifetime, especially fungible counters/pools with no per-grant owner.
  • Unbounded mint from compromised renderer
    Look for browser-side state a renderer can increment via repeated IPC without a revocation or cap, where dropping the pipe leaves residual authorization.
  • RAII revocation coverage
    Verify every ScopedClosureRunner/RAII-guarded capability is actually owned by an object whose destruction is guaranteed on both renderer and utility/service disconnect.
From 9a158bed51d4160609cbdc6242c338bfcc97e8f3 Mon Sep 17 00:00:00 2001
From: Sangbaek Park <sangbaekpark@google.com>
Date: Mon, 06 Jul 2026 17:02:04 -0700
Subject: [PATCH] media: Fix AudibilityBypassTracker authorization spoofing vulnerability

This patch addresses a security vulnerability where a compromised
renderer could mint an arbitrary number of audibility bypass grants by
repeatedly invoking `CreateMediaFoundationRenderer` and dropping the
pipes. These grants could then be used by unauthorized players to spoof
the tab audio indicator and acquire power wake-locks.

The fix resolves this by tightly coupling each audibility bypass grant
to the Mojo pipeline lifetime of the specific `MediaFoundationRenderer`.
A `MediaFoundationRendererExtensionProxy` now intercepts the extension
Mojo pipe and owns a move-only `ScopedGrant`. If either the renderer
or the utility process disconnects the pipe, the `ScopedGrant` is
destroyed and the bypass authorization is explicitly revoked. By moving
away from a fungible document-wide integer pool to a strict RAII
lifecycle tied directly to the extension pipe, spoofing attempts are
thwarted.

Unit tests added: {
MediaWebContentsObserverTest.GrantRevokedOnDisconnect }

Bug: 520002854
Change-Id: I34d8a8e41f183d5e1a5e148c55a8b8ae0380836a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7932763
Commit-Queue: Sangbaek Park <sangbaekpark@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1657586}
---
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index b7d670b..7557b2b 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -38,6 +38,7 @@
 #include "media/mojo/buildflags.h"
 #include "media/mojo/mojom/frame_interface_factory.mojom.h"
 #include "media/mojo/mojom/media_service.mojom.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 
@@ -294,6 +295,50 @@
 #endif  // BUILDFLAG(IS_WIN)
 };
 
+#if BUILDFLAG(IS_WIN)
+// Proxies the MediaFoundationRendererExtension to observe the lifetime of the
+// MediaFoundationRenderer in the utility process. When either the renderer
+// or the utility process disconnects, the audibility bypass grant is revoked.
+class MediaFoundationRendererExtensionProxy
+    : public media::mojom::MediaFoundationRendererExtension {
+ public:
+  MediaFoundationRendererExtensionProxy(
+      mojo::PendingRemote<media::mojom::MediaFoundationRendererExtension>
+          target_remote,
+      AudibilityBypassTracker::ScopedGrant grant)
+      : target_remote_(std::move(target_remote)), grant_(std::move(grant)) {
+    target_remote_.set_disconnect_handler(base::BindOnce(
+        &MediaFoundationRendererExtensionProxy::OnTargetDisconnect,
+        base::Unretained(this)));
+  }
+
+  ~MediaFoundationRendererExtensionProxy() override = default;
+
+  // media::mojom::MediaFoundationRendererExtension implementation.
+  void GetDCOMPSurface(GetDCOMPSurfaceCallback callback) override {
+    target_remote_->GetDCOMPSurface(std::move(callback));
+  }
+  void SetVideoStreamEnabled(bool enabled) override {
+    target_remote_->SetVideoStreamEnabled(enabled);
+  }
+  void SetOutputRect(const gfx::Rect& rect,
+                     SetOutputRectCallback callback) override {
+    target_remote_->SetOutputRect(rect, std::move(callback));
+  }
+
+ private:
+  void OnTargetDisconnect() {
+    // The utility process disconnected (MediaFoundationRenderer destroyed).
+    // Revoke the grant by resetting it.
+    grant_.RunAndReset();
+    target_remote_.reset();
+  }
+
+  mojo::Remote<media::mojom::MediaFoundationRendererExtension> target_remote_;
+  AudibilityBypassTracker::ScopedGrant grant_;
+};
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace
 
 MediaInterfaceProxy::MediaInterfaceProxy(RenderFrameHost* render_frame_host)
@@ -439,13 +484,24 @@
   // with a CDM path in CreateCdm().
   auto* factory = GetMediaFoundationServiceInterfaceFactory(base::FilePath());
   if (factory) {
-    factory->CreateMediaFoundationRenderer(
-        std::move(media_log_remote), std::move(receiver),
-        std::move(renderer_extension_receiver));
-
     // `MediaFoundationRenderer` bypasses the browser's audio service.
     // Authorize the frame for audibility bypass claims.
-    AudibilityBypassTracker::AddGrant(&render_frame_host());
+    AudibilityBypassTracker::ScopedGrant grant =
+        AudibilityBypassTracker::AddGrant(&render_frame_host());
+
+    mojo::PendingRemote<media::mojom::MediaFoundationRendererExtension>
+        utility_extension_remote;
+    auto utility_extension_receiver =
+        utility_extension_remote.InitWithNewPipeAndPassReceiver();
+
+    mojo::MakeSelfOwnedReceiver(
+        std::make_unique<MediaFoundationRendererExtensionProxy>(
+            std::move(utility_extension_remote), std::move(grant)),
+        std::move(renderer_extension_receiver));
+
+    factory->CreateMediaFoundationRenderer(
+        std::move(media_log_remote), std::move(receiver),
+        std::move(utility_extension_receiver));
   }
 }
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 2b7eba1..124085e 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -6,7 +6,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <set>
 #include <tuple>
 
 #include "base/debug/crash_logging.h"
@@ -655,6 +654,14 @@
   has_played_before_ = true;
 }
 
+void MediaWebContentsObserver::OnAudibilityBypassRevoked(
+    const MediaPlayerId& id) {
+  auto it = media_player_observer_hosts_.find(id);
+  if (it != media_player_observer_hosts_.end()) {
+    it->second->NotifyAudioStreamMonitorIfNeeded();
+  }
+}
+
 void MediaWebContentsObserver::OnAudioOutputSinkChangedWithRawDeviceId(
     const MediaPlayerId& player_id,
     const std::string& raw_device_id) {
@@ -821,10 +828,16 @@
 AudibilityBypassTracker::~AudibilityBypassTracker() = default;
 
 // static
-void AudibilityBypassTracker::AddGrant(RenderFrameHost* rfh) {
+AudibilityBypassTracker::ScopedGrant AudibilityBypassTracker::AddGrant(
+    RenderFrameHost* rfh) {
   if (rfh) {
-    GetOrCreateForCurrentDocument(rfh)->pending_grants_++;
+    auto* tracker = GetOrCreateForCurrentDocument(rfh);
+    int grant_id = ++tracker->next_grant_id_;
+    tracker->pending_grants_.insert(grant_id);
+    return ScopedGrant(base::BindOnce(&AudibilityBypassTracker::RevokeGrant,
+                                      rfh->GetGlobalId(), grant_id));
   }
+  return ScopedGrant();
 }
 
 // static
@@ -834,12 +847,13 @@
     return false;
   }
   auto* tracker = GetForCurrentDocument(rfh);
-  if (tracker && tracker->authorized_players_.contains(id)) {
+  if (tracker && tracker->active_grants_.contains(id)) {
     return true;
   }
-  if (tracker && tracker->pending_grants_ > 0) {
-    tracker->pending_grants_--;
-    tracker->authorized_players_.insert(id);
+  if (tracker && !tracker->pending_grants_.empty()) {
+    int grant_id = *tracker->pending_grants_.begin();
+    tracker->pending_grants_.erase(tracker->pending_grants_.begin());
+    tracker->active_grants_[id] = grant_id;
     return true;
   }
   return false;
@@ -853,7 +867,37 @@
   }
   auto* tracker = GetForCurrentDocument(rfh);
   if (tracker) {
-    tracker->authorized_players_.erase(id);
+    tracker->active_grants_.erase(id);
+  }
+}
+
+// static
+void AudibilityBypassTracker::RevokeGrant(GlobalRenderFrameHostId rfh_id,
+                                          int grant_id) {
+  auto* rfh = RenderFrameHost::FromID(rfh_id);
+  if (!rfh) {
+    return;
+  }
+  auto* tracker = GetForCurrentDocument(rfh);
+  if (!tracker) {
+    return;
+  }
+  if (tracker->pending_grants_.erase(grant_id)) {
+    return;
+  }
+  for (auto it = tracker->active_grants_.begin();
+       it != tracker->active_grants_.end(); ++it) {
+    if (it->second == grant_id) {
+      MediaPlayerId id = it->first;
+      tracker->active_grants_.erase(it);
+      auto* web_contents =
+          static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh));
+      if (web_contents && web_contents->media_web_contents_observer()) {
+        web_contents->media_web_contents_observer()->OnAudibilityBypassRevoked(
+            id);
+      }
+      break;
+    }
   }
 }
 
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h
index c00bd80..654323f 100644
--- a/content/browser/media/media_web_contents_observer.h
+++ b/content/browser/media/media_web_contents_observer.h
@@ -13,6 +13,8 @@
 
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
@@ -71,7 +73,9 @@
  public:
   ~AudibilityBypassTracker() override;
 
-  static void AddGrant(RenderFrameHost* rfh);
+  using ScopedGrant = base::ScopedClosureRunner;
+
+  static ScopedGrant AddGrant(RenderFrameHost* rfh);
   static bool ClaimGrant(const MediaPlayerId& id);
   static void ReleaseGrant(const MediaPlayerId& id);
 
@@ -80,8 +84,11 @@
   explicit AudibilityBypassTracker(RenderFrameHost* rfh);
   DOCUMENT_USER_DATA_KEY_DECL();
 
-  size_t pending_grants_ = 0;
-  base::flat_set<MediaPlayerId> authorized_players_;
+  static void RevokeGrant(GlobalRenderFrameHostId rfh_id, int grant_id);
+
+  int next_grant_id_ = 0;
+  base::flat_set<int> pending_grants_;
+  base::flat_map<MediaPlayerId, int> active_grants_;
 };
 
 // This class manages all RenderFrame based media related managers at the
@@ -126,6 +133,9 @@
   void RenderFrameHostChanged(RenderFrameHost* old_host,
                               RenderFrameHost* new_host) override;
 
+  // Called when an audibility bypass grant is revoked.
+  void OnAudibilityBypassRevoked(const MediaPlayerId& id);
+
   // MediaPlayerObserverClient implementation.
   void GetHasPlayedBefore(GetHasPlayedBeforeCallback callback) override;
 
@@ -248,10 +258,10 @@
         media_session::mojom::RemotePlaybackMetadataPtr
             remote_playback_metadata) override;
     void OnVideoVisibilityChanged(bool meets_visibility_threshold) override;
+    void NotifyAudioStreamMonitorIfNeeded();
 
    private:
     PlayerInfo* GetPlayerInfo();
-    void NotifyAudioStreamMonitorIfNeeded();
 
     void OnReceivedMediaDeviceSalt(
         const std::string& hashed_device_id,
diff --git a/content/browser/media/media_web_contents_observer_unittest.cc b/content/browser/media/media_web_contents_observer_unittest.cc
index 80f57f7f..b0cb6f01 100644
--- a/content/browser/media/media_web_contents_observer_unittest.cc
+++ b/content/browser/media/media_web_contents_observer_unittest.cc
@@ -363,7 +363,8 @@
   auto player = CreateAndAddPlayer(player_host);
 
   // Simulate audibility bypass authorization for the document.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   SetMediaMetadata(player.observer, /*has_audio=*/true, /*has_video=*/false);
 
@@ -395,7 +396,7 @@
   auto child_player = CreateAndAddPlayer(child_player_host);
 
   // Authorize only the main frame.
-  AudibilityBypassTracker::AddGrant(main_rfh);
+  auto grant = AudibilityBypassTracker::AddGrant(main_rfh);
 
   // Child frame attempts bypass.
   SetMediaMetadata(child_player.observer, /*has_audio=*/true,
@@ -422,7 +423,8 @@
   auto original_player = CreateAndAddPlayer(player_host);
 
   // Simulate MediaFoundationRenderer creation grant.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   // Original player claims the grant successfully.
   SetMediaMetadata(original_player.observer, true, false);
@@ -456,8 +458,10 @@
   auto player_host = SetupPlayerHost();
 
   // Simulate two MediaFoundationRenderer creation grants.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant1 =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant2 =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   auto player1 = CreateAndAddPlayer(player_host);
   auto player2 = CreateAndAddPlayer(player_host);
@@ -489,5 +493,30 @@
   EXPECT_FALSE(IsWebContentsAudible());
 }
 
+// This test reproduces the issue where an attacker can mint bypass grants.
+// By creating grants and then disconnecting the pipe, the grant should be
+// automatically revoked, preventing the bypass from being exploited.
+TEST_F(MediaWebContentsObserverTest, GrantRevokedOnDisconnect) {
+  RenderFrameHost* rfh = contents()->GetPrimaryMainFrame();
+  MediaPlayerId player_id(rfh->GetGlobalId(), 1);
+
+  // Simulate a compromised renderer requesting MediaFoundationRenderer
+  // creation, which mints a new bypass grant.
+  auto grant = AudibilityBypassTracker::AddGrant(rfh);
+
+  // The player can successfully claim the grant.
+  EXPECT_TRUE(AudibilityBypassTracker::ClaimGrant(player_id));
+
+  // In the exploit scenario, the attacker disconnects the dummy Mojo pipe
+  // but attempts to continue using the grant.
+  // With the fix, the disconnection automatically calls RevokeGrant.
+  grant.RunAndReset();
+
+  // After revocation, the player should be unregistered from the bypass
+  // authorization. Next time it tries to claim, it should fail.
+  AudibilityBypassTracker::ReleaseGrant(player_id);
+  EXPECT_FALSE(AudibilityBypassTracker::ClaimGrant(player_id));
+}
+
 }  // namespace
 }  // namespace content
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/media/media_web_contents_observer_unittest.cc b/content/browser/media/media_web_contents_observer_unittest.cc
index 80f57f7f..b0cb6f01 100644
--- a/content/browser/media/media_web_contents_observer_unittest.cc
+++ b/content/browser/media/media_web_contents_observer_unittest.cc
@@ -363,7 +363,8 @@
   auto player = CreateAndAddPlayer(player_host);
 
   // Simulate audibility bypass authorization for the document.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   SetMediaMetadata(player.observer, /*has_audio=*/true, /*has_video=*/false);
 
@@ -395,7 +396,7 @@
   auto child_player = CreateAndAddPlayer(child_player_host);
 
   // Authorize only the main frame.
-  AudibilityBypassTracker::AddGrant(main_rfh);
+  auto grant = AudibilityBypassTracker::AddGrant(main_rfh);
 
   // Child frame attempts bypass.
   SetMediaMetadata(child_player.observer, /*has_audio=*/true,
@@ -422,7 +423,8 @@
   auto original_player = CreateAndAddPlayer(player_host);
 
   // Simulate MediaFoundationRenderer creation grant.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   // Original player claims the grant successfully.
   SetMediaMetadata(original_player.observer, true, false);
@@ -456,8 +458,10 @@
   auto player_host = SetupPlayerHost();
 
   // Simulate two MediaFoundationRenderer creation grants.
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
-  AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant1 =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
+  auto grant2 =
+      AudibilityBypassTracker::AddGrant(contents()->GetPrimaryMainFrame());
 
   auto player1 = CreateAndAddPlayer(player_host);
   auto player2 = CreateAndAddPlayer(player_host);
@@ -489,5 +493,30 @@
   EXPECT_FALSE(IsWebContentsAudible());
 }
 
+// This test reproduces the issue where an attacker can mint bypass grants.
+// By creating grants and then disconnecting the pipe, the grant should be
+// automatically revoked, preventing the bypass from being exploited.
+TEST_F(MediaWebContentsObserverTest, GrantRevokedOnDisconnect) {
+  RenderFrameHost* rfh = contents()->GetPrimaryMainFrame();
+  MediaPlayerId player_id(rfh->GetGlobalId(), 1);
+
+  // Simulate a compromised renderer requesting MediaFoundationRenderer
+  // creation, which mints a new bypass grant.
+  auto grant = AudibilityBypassTracker::AddGrant(rfh);
+
+  // The player can successfully claim the grant.
+  EXPECT_TRUE(AudibilityBypassTracker::ClaimGrant(player_id));
+
+  // In the exploit scenario, the attacker disconnects the dummy Mojo pipe
+  // but attempts to continue using the grant.
+  // With the fix, the disconnection automatically calls RevokeGrant.
+  grant.RunAndReset();
+
+  // After revocation, the player should be unregistered from the bypass
+  // authorization. Next time it tries to claim, it should fail.
+  AudibilityBypassTracker::ReleaseGrant(player_id);
+  EXPECT_FALSE(AudibilityBypassTracker::ClaimGrant(player_id));
+}
+
 }  // namespace
 }  // namespace content
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.