Chrome · BrowserTag
CVE-2026-113269
Memory Corruption in BrowserTag
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
forchrome/test/data/webui/webview/webui_webview_browsertest.cc |
modified | |
suitechrome/test/data/webui/webview/webview_untrusted_test.ts |
modified |
Files Changed
extensions/browser/guest_view/web_view/web_view_permission_helper.ccchrome/test/data/webui/webview/webview_untrusted_test.tschrome/test/data/webui/webview/webui_webview_browsertest.cc
Patch
From 8d17e06dbdfe0a7c0e8042a78ee0328c1f55cc46 Mon Sep 17 00:00:00 2001
From: Kevin McNee <mcnee@chromium.org>
Date: Tue, 22 Sep 2026 15:17:50 -0700
Subject: [PATCH] Prevent chrome-untrusted from approving webview permissions
Similar to https://crbug.com/502179319 if a webview has a
chrome-untrusted page as an embedder, it should not be trusted to grant
permissions.
Fixed: 553334273
Change-Id: Ic1461da87e31c75786c8742a45e45bf07079e4ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8431428
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Auto-Submit: Kevin McNee <mcnee@chromium.org>
Commit-Queue: Kevin McNee <mcnee@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1702992}
---
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index 693c97f..d3bfc80e 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -440,6 +440,19 @@
return;
}
+ // A chrome-untrusted embedder can't be trusted to approve permissions.
+ // New Window and Dialog are excluded, since they reuse this path but aren't
+ // really permissions.
+ if (permission_type != WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW &&
+ permission_type != WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG &&
+ web_view_guest_->GetOwnerSiteURL().SchemeIs(
+ content::kChromeUIUntrustedScheme)) {
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback), allowed_by_default, std::string()));
+ return;
+ }
+
int request_id = next_permission_request_id_++;
pending_permission_requests_[request_id] = PermissionResponseInfo(
std::move(callback), permission_type, allowed_by_default);
diff --git a/chrome/test/data/webui/webview/webui_webview_browsertest.cc b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
index c128fd95..0993a424 100644
--- a/chrome/test/data/webui/webview/webui_webview_browsertest.cc
+++ b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
@@ -30,10 +30,12 @@
#include "chrome/test/base/test_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/web_ui_mocha_browser_test.h"
+#include "components/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
#include "components/webui/chrome_urls/pref_names.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/context_menu_params.h"
+#include "content/public/browser/download_manager.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
@@ -551,6 +553,10 @@
WebUIMochaBrowserTest::SetUpOnMainThread();
}
+ GURL GetTestUrl(const std::string& path) const {
+ return embedded_test_server()->base_url().Resolve(path);
+ }
+
content::WebContents* GetWebContentsForTesting() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
@@ -568,4 +574,25 @@
true));
}
+IN_PROC_BROWSER_TEST_F(UntrustedWebUIWebViewBrowserTest,
+ PermissionRequestAutoDeny) {
+ EXPECT_TRUE(RunTestOnWebContents(
+ GetWebContentsForTesting(), "webview/webview_untrusted_test.js",
+ base::StringPrintf("window.downloadUrl = '%s'; "
+ "runMochaTest('WebviewUntrustedBasicTest', "
+ "'PermissionRequestAutoDenyInUntrusted');",
+ GetTestUrl("download-test3.gif").spec().c_str()),
+ true));
+
+ content::DownloadManager::DownloadVector downloads;
+ browser()->GetProfile()->GetDownloadManager()->GetAllDownloads(&downloads);
+ EXPECT_TRUE(downloads.empty());
+ // If the test fails and a download was started, cancel it so that an
+ // in-progress download prompt does not block browser shutdown during test
+ // teardown.
+ for (download::DownloadItem* download : downloads) {
+ download->Cancel(false);
+ }
+}
+
#endif // !BUILDFLAG(IS_MAC)
diff --git a/chrome/test/data/webui/webview/webview_untrusted_test.ts b/chrome/test/data/webui/webview/webview_untrusted_test.ts
index 2c504f66..2ce33ef 100644
--- a/chrome/test/data/webui/webview/webview_untrusted_test.ts
+++ b/chrome/test/data/webui/webview/webview_untrusted_test.ts
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assertThrows} from 'chrome-untrusted://webui-test/chai_assert.js';
+import {assertFalse, assertThrows} from 'chrome-untrusted://webui-test/chai_assert.js';
suite('WebviewUntrustedBasicTest', function() {
function createWebview(): chrome.webviewTag.WebView {
@@ -12,6 +12,10 @@
return webview;
}
+ function getDownloadUrl(): string {
+ return (window as unknown as Window & {downloadUrl: string}).downloadUrl;
+ }
+
test('BannedApisThrowInUntrusted', async () => {
const webview = createWebview();
await new Promise(resolve => {
@@ -37,4 +41,21 @@
webview.loadDataWithBaseUrl('data:text/html,test', 'https://example.com');
});
});
+
+ test('PermissionRequestAutoDenyInUntrusted', async () => {
+ const webview = createWebview();
+ let receivedPermissionRequest = false;
+ webview.addEventListener('permissionrequest', event => {
+ receivedPermissionRequest = true;
+ const e = event as chrome.webviewTag.PermissionRequestEvent;
+ e.request.allow();
+ });
+
+ await new Promise(resolve => {
+ webview.src = getDownloadUrl();
+ webview.addEventListener('loadstop', resolve);
+ });
+
+ assertFalse(receivedPermissionRequest);
+ });
});
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/test/data/webui/webview/webui_webview_browsertest.cc b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
index c128fd95..0993a424 100644
--- a/chrome/test/data/webui/webview/webui_webview_browsertest.cc
+++ b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
@@ -30,10 +30,12 @@
#include "chrome/test/base/test_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/web_ui_mocha_browser_test.h"
+#include "components/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
#include "components/webui/chrome_urls/pref_names.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/context_menu_params.h"
+#include "content/public/browser/download_manager.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
@@ -551,6 +553,10 @@
WebUIMochaBrowserTest::SetUpOnMainThread();
}
+ GURL GetTestUrl(const std::string& path) const {
+ return embedded_test_server()->base_url().Resolve(path);
+ }
+
content::WebContents* GetWebContentsForTesting() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
@@ -568,4 +574,25 @@
true));
}
+IN_PROC_BROWSER_TEST_F(UntrustedWebUIWebViewBrowserTest,
+ PermissionRequestAutoDeny) {
+ EXPECT_TRUE(RunTestOnWebContents(
+ GetWebContentsForTesting(), "webview/webview_untrusted_test.js",
+ base::StringPrintf("window.downloadUrl = '%s'; "
+ "runMochaTest('WebviewUntrustedBasicTest', "
+ "'PermissionRequestAutoDenyInUntrusted');",
+ GetTestUrl("download-test3.gif").spec().c_str()),
+ true));
+
+ content::DownloadManager::DownloadVector downloads;
+ browser()->GetProfile()->GetDownloadManager()->GetAllDownloads(&downloads);
+ EXPECT_TRUE(downloads.empty());
+ // If the test fails and a download was started, cancel it so that an
+ // in-progress download prompt does not block browser shutdown during test
+ // teardown.
+ for (download::DownloadItem* download : downloads) {
+ download->Cancel(false);
+ }
+}
+
#endif // !BUILDFLAG(IS_MAC)
diff --git a/chrome/test/data/webui/webview/webview_untrusted_test.ts b/chrome/test/data/webui/webview/webview_untrusted_test.ts
index 2c504f66..2ce33ef 100644
--- a/chrome/test/data/webui/webview/webview_untrusted_test.ts
+++ b/chrome/test/data/webui/webview/webview_untrusted_test.ts
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assertThrows} from 'chrome-untrusted://webui-test/chai_assert.js';
+import {assertFalse, assertThrows} from 'chrome-untrusted://webui-test/chai_assert.js';
suite('WebviewUntrustedBasicTest', function() {
function createWebview(): chrome.webviewTag.WebView {
@@ -12,6 +12,10 @@
return webview;
}
+ function getDownloadUrl(): string {
+ return (window as unknown as Window & {downloadUrl: string}).downloadUrl;
+ }
+
test('BannedApisThrowInUntrusted', async () => {
const webview = createWebview();
await new Promise(resolve => {
@@ -37,4 +41,21 @@
webview.loadDataWithBaseUrl('data:text/html,test', 'https://example.com');
});
});
+
+ test('PermissionRequestAutoDenyInUntrusted', async () => {
+ const webview = createWebview();
+ let receivedPermissionRequest = false;
+ webview.addEventListener('permissionrequest', event => {
+ receivedPermissionRequest = true;
+ const e = event as chrome.webviewTag.PermissionRequestEvent;
+ e.request.allow();
+ });
+
+ await new Promise(resolve => {
+ webview.src = getDownloadUrl();
+ webview.addEventListener('loadstop', resolve);
+ });
+
+ assertFalse(receivedPermissionRequest);
+ });
});
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