Low chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper privilege management in UI
DescriptionImproper privilege management in UI
ComponentUI
Bug ClassAuthorization Error
Tracker540078886
Fix commit9030e52c0a6d (chromium/src) +74/-10
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/html/media/html_media_element.cc
modified
TEST_P
third_party/blink/renderer/core/html/media/html_media_element_test.cc
modified
TEST_F
third_party/blink/renderer/modules/mediasession/media_session_test.cc
modified

Files Changed

  • third_party/blink/renderer/modules/mediasession/media_session_test.cc
  • third_party/blink/renderer/modules/mediasession/media_session.cc
  • third_party/blink/renderer/core/html/media/html_media_element_test.cc
  • third_party/blink/renderer/core/html/media/html_media_element.cc
From 9030e52c0a6d90dd8ae7789853a531440dfdab24 Mon Sep 17 00:00:00 2001
From: Tommy Steimel <steimel@chromium.org>
Date: Tue, 08 Sep 2026 15:32:14 -0700
Subject: [PATCH] Media Session: Don't grant user activation for pause actions

Currently, user activation is granted when a page receives any media
session action, which allows the page to play a video, put a video into
a picture-in-picture window, etc. However, this activation is not
actually needed for pause and stop actions, and only serves as a
potential abuse vector.

This CL changes Media Session to no longer grant user activation for
pause and stop actions.

Bug: 540078886
Change-Id: I8f6c355e2d279ea1af2ef0e5631de17a5271e7b4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8373882
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1694124}
---
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 2a21c362..550e100f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -5334,13 +5334,9 @@
 }
 
 void HTMLMediaElement::RequestPause(bool triggered_by_user) {
-  if (triggered_by_user) {
-    LocalFrame* frame = GetDocument().GetFrame();
-    if (frame) {
-      LocalFrame::NotifyUserActivation(
-          frame, mojom::blink::UserActivationNotificationType::kInteraction);
-    }
-  }
+  // Never grant user activation for pause actions. Pausing media never requires
+  // user activation and should not allow websites to trigger restricted APIs
+  // like popups or clipboard writes on pause events.
   PauseInternal(triggered_by_user
                     ? WebMediaPlayer::PauseReason::kPauseRequestedByUser
                     : WebMediaPlayer::PauseReason::kPauseRequestedInternally);
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.cc b/third_party/blink/renderer/modules/mediasession/media_session.cc
index 8c95e017..05765dc8 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session.cc
@@ -427,9 +427,16 @@
   LocalDOMWindow* window = GetSupplementable()->DomWindow();
   if (!window)
     return;
-  LocalFrame::NotifyUserActivation(
-      window->GetFrame(),
-      mojom::blink::UserActivationNotificationType::kInteraction);
+  // Do not grant user activation for pause or stop actions. Pausing or stopping
+  // media never requires user activation and should not allow websites to
+  // trigger restricted APIs (e.g. popups or clipboard access) via pause events
+  // or system/accessory actions like removing headphones.
+  if (action != media_session::mojom::blink::MediaSessionAction::kPause &&
+      action != media_session::mojom::blink::MediaSessionAction::kStop) {
+    LocalFrame::NotifyUserActivation(
+        window->GetFrame(),
+        mojom::blink::UserActivationNotificationType::kInteraction);
+  }
 
   auto v8_action = MojomActionToActionEnum(action);
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index d26b8e7..d9ff029 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -67,6 +67,11 @@
       triggered_by_user);
 }
 
+void SimulateRequestPause(HTMLMediaElement* element, bool triggered_by_user) {
+  static_cast<media::mojom::blink::MediaPlayer*>(element)->RequestPause(
+      triggered_by_user);
+}
+
 enum class TestURLScheme {
   kHttp,
   kHttps,
@@ -2301,6 +2306,39 @@
       Media()->GetDocument().GetFrame()));
 }
 
+TEST_P(HTMLMediaElementTest, RequestPause_UserTriggered) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  EXPECT_CALL(*MockMediaPlayer(), Paused()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*MockMediaPlayer(),
+              Pause(WebMediaPlayer::PauseReason::kPauseRequestedByUser));
+  SimulateRequestPause(Media(), /*triggered_by_user=*/true);
+
+  // The frame should NOT be granted transient user activation even when
+  // triggered by the user.
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(
+      Media()->GetDocument().GetFrame()));
+}
+
+TEST_P(HTMLMediaElementTest, RequestPause_SystemTriggered) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  EXPECT_CALL(*MockMediaPlayer(), Paused()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*MockMediaPlayer(),
+              Pause(WebMediaPlayer::PauseReason::kPauseRequestedInternally));
+  SimulateRequestPause(Media(), /*triggered_by_user=*/false);
+
+  // The frame should NOT be granted transient user activation.
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(
+      Media()->GetDocument().GetFrame()));
+}
+
 TEST_P(HTMLMediaElementTest, PlayedWithUserActivation) {
   Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
   test::RunPendingTasks();
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_test.cc b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
index 796f060..e6c43d96 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session_test.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
@@ -117,6 +117,11 @@
 
   base::SimpleTestTickClock& clock() { return test_clock_; }
 
+  void DidReceiveAction(
+      media_session::mojom::blink::MediaSessionAction action) {
+    media_session_->DidReceiveAction(action, nullptr);
+  }
+
  private:
   base::SimpleTestTickClock test_clock_;
 
@@ -389,4 +394,22 @@
   SetPlaybackState(V8MediaSessionPlaybackState::Enum::kPaused);
 }
 
+TEST_F(MediaSessionTest, DidReceiveAction_PlayGrantsUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kPlay);
+  EXPECT_TRUE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
+TEST_F(MediaSessionTest, DidReceiveAction_PauseDoesNotGrantUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kPause);
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
+TEST_F(MediaSessionTest, DidReceiveAction_StopDoesNotGrantUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kStop);
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
 }  // namespace blink
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index d26b8e7..d9ff029 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -67,6 +67,11 @@
       triggered_by_user);
 }
 
+void SimulateRequestPause(HTMLMediaElement* element, bool triggered_by_user) {
+  static_cast<media::mojom::blink::MediaPlayer*>(element)->RequestPause(
+      triggered_by_user);
+}
+
 enum class TestURLScheme {
   kHttp,
   kHttps,
@@ -2301,6 +2306,39 @@
       Media()->GetDocument().GetFrame()));
 }
 
+TEST_P(HTMLMediaElementTest, RequestPause_UserTriggered) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  EXPECT_CALL(*MockMediaPlayer(), Paused()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*MockMediaPlayer(),
+              Pause(WebMediaPlayer::PauseReason::kPauseRequestedByUser));
+  SimulateRequestPause(Media(), /*triggered_by_user=*/true);
+
+  // The frame should NOT be granted transient user activation even when
+  // triggered by the user.
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(
+      Media()->GetDocument().GetFrame()));
+}
+
+TEST_P(HTMLMediaElementTest, RequestPause_SystemTriggered) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  EXPECT_CALL(*MockMediaPlayer(), Paused()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*MockMediaPlayer(),
+              Pause(WebMediaPlayer::PauseReason::kPauseRequestedInternally));
+  SimulateRequestPause(Media(), /*triggered_by_user=*/false);
+
+  // The frame should NOT be granted transient user activation.
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(
+      Media()->GetDocument().GetFrame()));
+}
+
 TEST_P(HTMLMediaElementTest, PlayedWithUserActivation) {
   Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
   test::RunPendingTasks();
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_test.cc b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
index 796f060..e6c43d96 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session_test.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
@@ -117,6 +117,11 @@
 
   base::SimpleTestTickClock& clock() { return test_clock_; }
 
+  void DidReceiveAction(
+      media_session::mojom::blink::MediaSessionAction action) {
+    media_session_->DidReceiveAction(action, nullptr);
+  }
+
  private:
   base::SimpleTestTickClock test_clock_;
 
@@ -389,4 +394,22 @@
   SetPlaybackState(V8MediaSessionPlaybackState::Enum::kPaused);
 }
 
+TEST_F(MediaSessionTest, DidReceiveAction_PlayGrantsUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kPlay);
+  EXPECT_TRUE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
+TEST_F(MediaSessionTest, DidReceiveAction_PauseDoesNotGrantUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kPause);
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
+TEST_F(MediaSessionTest, DidReceiveAction_StopDoesNotGrantUserActivation) {
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+  DidReceiveAction(media_session::mojom::blink::MediaSessionAction::kStop);
+  EXPECT_FALSE(LocalFrame::HasTransientUserActivation(&GetFrame()));
+}
+
 }  // namespace blink
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.