Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Downloads
DescriptionIncorrect authorization in Downloads
ComponentDownloads
Bug ClassLogic Error
Tracker496616790
Fix commit0d84f06e082c (chromium/src) +9/-10
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Files Changed

  • content/browser/download/download_browsertest.cc
  • content/browser/renderer_host/render_frame_host_impl.cc
  • third_party/blink/public/mojom/frame/frame.mojom
  • third_party/blink/renderer/core/frame/local_frame.cc
  • third_party/blink/renderer/core/html/media/html_video_element.cc
  • third_party/blink/renderer/core/html/media/html_video_element_test.cc
From 0d84f06e082c9d50591ae52473a1f8afbd6985df Mon Sep 17 00:00:00 2001
From: Svend L <svend@chromium.org>
Date: Fri, 14 Aug 2026 11:15:45 -0700
Subject: [PATCH] [Downloads] Rename is_context_menu_save to should_prompt_for_save_location

After crrev.com/c/8177729, DownloadURLParams::is_context_menu_save is
only used to determine whether the filepicker should be shown for a
download. Since the param is supplied by the renderer, a compromised
renderer can lie about whether a download is initiated from a context
menu. Rename the param to reflect its narrower scope, and to discourage
the browser from relying on the param value to indicate download source
in the future.

Bug: 496616790
Change-Id: Ic5a1277c2ded0671b3f72f9ae004d81a6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8204664
Reviewed-by: Charlie Reis <creis@chromium.org>
Reviewed-by: Giovanni Ortuno Urquidi <ortuno@chromium.org>
Reviewed-by: Nate Chapin <japhet@chromium.org>
Reviewed-by: Ari Chivukula <arichiv@chromium.org>
Reviewed-by: Min Qin <qinmin@chromium.org>
Commit-Queue: Svend L <svend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1679710}
---

diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 5f0208a..9e57315f 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -5446,7 +5446,7 @@
 
   auto params = blink::mojom::DownloadURLParams::New();
   params->url = kDownloadUrl;
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   static_cast<RenderFrameHostImpl*>(render_frame_host)
       ->DownloadURL(std::move(params));
 
@@ -5520,7 +5520,7 @@
 
   auto params = blink::mojom::DownloadURLParams::New();
   params->url = kDownloadUrl;
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   static_cast<RenderFrameHostImpl*>(fenced_frame_host)
       ->DownloadURL(std::move(params));
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index e038883..c8440e1 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8003,14 +8003,13 @@
   std::unique_ptr<download::DownloadUrlParameters> parameters =
       CreateDownloadUrlParameters(blink_parameters->url, traffic_annotation);
   // Downloads arriving through this IPC handler always originate from web
-  // content, so treat them as content-initiated regardless of what the
-  // renderer reports in `is_context_menu_save`.
+  // content.
   parameters->set_content_initiated(true);
   parameters->set_has_user_gesture(blink_parameters->has_user_gesture &&
                                    HasTransientUserActivation());
   parameters->set_suggested_name(
       blink_parameters->suggested_name.value_or(std::u16string()));
-  parameters->set_prompt(blink_parameters->is_context_menu_save);
+  parameters->set_prompt(blink_parameters->should_prompt_for_save_location);
   parameters->set_cross_origin_redirects(
       blink_parameters->cross_origin_redirects);
   parameters->set_referrer(
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 5db65454..579f2fd 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -142,8 +142,8 @@
   // Non-null when |url| is for "data:", eg. when saving an image.
   pending_remote<Blob>? data_url_blob;
 
-  // Whether the download is from context menu.
-  bool is_context_menu_save = false;
+  // Whether to prompt the user for a destination filepath.
+  bool should_prompt_for_save_location = false;
 
   // True when the download was preceded by a recent gesture from the user.
   bool has_user_gesture = false;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 43eb225..f049ab6f 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -3867,7 +3867,7 @@
   }
 
   auto params = mojom::blink::DownloadURLParams::New();
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   params->data_url_blob = DataURLToBlob(url);
   GetLocalFrameHostRemote().DownloadURL(std::move(params));
 }
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index d47b1d93..e07540f 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -574,7 +574,7 @@
       /*is_top_level_navigation=*/false);
 
   auto params = mojom::blink::DownloadURLParams::New();
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   auto timestamp_ms = base::saturated_cast<uint32_t>(
       currentTime() * base::Time::kMillisecondsPerSecond);
   params->suggested_name =
diff --git a/third_party/blink/renderer/core/html/media/html_video_element_test.cc b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
index 18d3482b..e7bfeee 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
@@ -710,7 +710,7 @@
   ASSERT_TRUE(FrameHost()->download_url_called());
   const auto& params = FrameHost()->download_params();
   ASSERT_TRUE(params);
-  EXPECT_TRUE(params->is_context_menu_save);
+  EXPECT_TRUE(params->should_prompt_for_save_location);
   EXPECT_TRUE(params->suggested_name.starts_with("videoframe_"));
   EXPECT_TRUE(params->url.ProtocolIs("blob"));
   EXPECT_TRUE(params->blob_url_token.is_valid());
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 5f0208a..9e57315f 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -5446,7 +5446,7 @@
 
   auto params = blink::mojom::DownloadURLParams::New();
   params->url = kDownloadUrl;
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   static_cast<RenderFrameHostImpl*>(render_frame_host)
       ->DownloadURL(std::move(params));
 
@@ -5520,7 +5520,7 @@
 
   auto params = blink::mojom::DownloadURLParams::New();
   params->url = kDownloadUrl;
-  params->is_context_menu_save = true;
+  params->should_prompt_for_save_location = true;
   static_cast<RenderFrameHostImpl*>(fenced_frame_host)
       ->DownloadURL(std::move(params));
diff --git a/third_party/blink/renderer/core/html/media/html_video_element_test.cc b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
index 18d3482b..e7bfeee 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element_test.cc
@@ -710,7 +710,7 @@
   ASSERT_TRUE(FrameHost()->download_url_called());
   const auto& params = FrameHost()->download_params();
   ASSERT_TRUE(params);
-  EXPECT_TRUE(params->is_context_menu_save);
+  EXPECT_TRUE(params->should_prompt_for_save_location);
   EXPECT_TRUE(params->suggested_name.starts_with("videoframe_"));
   EXPECT_TRUE(params->url.ProtocolIs("blob"));
   EXPECT_TRUE(params->blob_url_token.is_valid());
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.