Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Browser
DescriptionIncorrect authorization in Browser
ComponentBrowser
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.
Tracker520201931
Fix commitc2ca57044b1f (chromium/src) +73/-1
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-05 (per vendor advisory)
Fix landed2026-07-30 (public git)
Disclosed2026-09-08
Patch gap40 days — fix public in source before the advisory

Background

`ChromeContentBrowserClient::CanCreateWindow()`
the browser-process embedder hook Chrome consults to decide whether a renderer’s window.open() request may create a new window; the commit message names it as the function edited (its name is absent from the @@ hunk header, so its identity comes from the commit message, not the diff context).
`WindowContainerType::BACKGROUND`
the window container type requested when web content calls window.open(url, name, 'background'), historically an extensions-only feature for opening non-visible background windows.
`ENABLE_EXTENSIONS_CORE`
a BUILDFLAG that is true on builds with extension support (Desktop) and false on non-extension builds such as Chrome for Android, gating the extension-permission logic in this code path.
Popup blocker
the browser mechanism that normally rejects programmatic window creation, which per the added test’s comment does not run for a 'background' request made with transient user activation, leaving CanCreateWindow() as the deciding authority.

Root Cause Analysis

On non-extension builds (ENABLE_EXTENSIONS_CORE false), the WindowContainerType::BACKGROUND branch of CanCreateWindow() fell through to return true unconditionally, as the removed #endif placement and the surviving return true show. The commit message states this let ordinary web content open a background window via window.open(url, name, 'background'), bypassing popup blocking. The invariant violated is that a BACKGROUND window is an extension-gated capability: on Desktop, web content lacking the extension background permission is rejected, but on non-extension builds there is no extension to check, so the code granted the request by default instead of denying it.

The fix moves the #endif and adds an #else arm that returns false, so when ENABLE_EXTENSIONS_CORE is false the BACKGROUND request is now explicitly refused. This works because it converts a default-allow into a default-deny for the exact configuration that had no permission check to fall back on.

Key insight
The single mistake was treating an extension-gated window type as allowed-by-default on builds that compile out the extension permission logic entirely; the fix closes the gap by returning false in the #else branch so a missing extension check means denial rather than approval.

Attack Path

  1. Reach the vulnerable configuration The victim runs a non-extension Chrome build (e.g. Chrome for Android) where ENABLE_EXTENSIONS_CORE is false, so the BACKGROUND branch of CanCreateWindow() has no extension to consult.
  2. Obtain user activation Ordinary web content secures transient user activation (a click), which per the test’s comment is required so the standard popup blocker does not reject the call before it reaches CanCreateWindow().
  3. Request a background window The page calls window.open('/some.html', '', 'background'); the request maps to WindowContainerType::BACKGROUND and, pre-patch, CanCreateWindow() returns true, so a new WebContents/window is created despite the page having no extension background permission — the popup-blocking bypass the commit message describes.
  4. No proven escalation beyond window creation The diff only shows creation of an otherwise-blocked window; any further abuse (background/off-screen navigation, resource use, or user deception) is not demonstrated by the patch. (inferred)

Impact Assessment

An attacker-controlled web page on a non-extension build gains the ability to bypass popup blocking and create a BACKGROUND window from ordinary web content in the renderer/web context, with the only precondition being transient user activation. The vendor advisory rates this medium severity and does not report in-the-wild exploitation; NVD scores it CVSS 6.5. Any consequence beyond the unauthorized window creation the patch prevents — such as user deception or resource abuse — is (inferred) and not established by the diff.

Changed Functions

FunctionChangeNotes
ChromeContentBrowserClientPlatformBrowserTest
chrome/browser/chrome_content_browser_client_platform_browsertest.cc
modified

Files Changed

  • chrome/browser/chrome_content_browser_client_platform_browsertest.cc
  • chrome/test/BUILD.gn
  • chrome/browser/chrome_content_browser_client.cc

Audit Directions

  • Default-allow on compiled-out permission checks
    Search for embedder hooks that gate a capability on an extension/feature permission inside a BUILDFLAG guard and return true (or otherwise allow) in the fall-through when that flag is false; the missing check should mean deny, not allow.
  • `#if`/`#else`/`#endif` fall-through returns
    Review authorization functions where a security decision sits just after a preprocessor block, since a misplaced #endif can silently let a permissive return cover the disabled-feature configuration.
  • Window/container type gating in `CanCreateWindow` and siblings
    Audit other WindowContainerType values and other content::ContentBrowserClient authorization overrides on non-extension builds to confirm each privileged type is explicitly denied to ordinary web content rather than allowed by default.
From c2ca57044b1fe69ab46bff64e914f50a389e59d6 Mon Sep 17 00:00:00 2001
From: Aman Verma <amanvr@google.com>
Date: Thu, 30 Jul 2026 10:12:19 -0700
Subject: [PATCH] Disallow background window container type on non-extension builds

ChromeContentBrowserClient::CanCreateWindow() previously returned true
unconditionally for WindowContainerType::BACKGROUND on non-extension
builds (such as Chrome for Android) where ENABLE_EXTENSIONS_CORE is
false. This allowed ordinary web content to bypass popup blocking when
calling window.open(url, name, 'background').

This change explicitly returns false for WindowContainerType::BACKGROUND
when ENABLE_EXTENSIONS_CORE is false, matching the behavior on Desktop
where ordinary web content lacking the extension background permission
is rejected.

A PlatformBrowserTest is added to platform_browser_tests (included in
both browser_tests and android_browsertests) to assert that calling
window.open(..., 'background') from ordinary web content returns null,
even when executed with transient user activation. Furthermore, it
verifies that no WebContents is created and includes a positive control
assertion that a standard popup without 'background' succeeds.

Bug: 520201931
Change-Id: I7b532a41121c6e2fb3eec984cfd88a5e37149d51
Fixed: 520201931
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8176283
Commit-Queue: Aman Verma <amanvr@google.com>
Reviewed-by: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1671184}
---
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index b121c619..dd11264 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4518,9 +4518,11 @@
     if (extension && !extensions::BackgroundInfo::AllowJSAccess(extension)) {
       *no_javascript_access = true;
     }
-#endif
 
     return true;
+#else
+    return false;
+#endif
   }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
diff --git a/chrome/browser/chrome_content_browser_client_platform_browsertest.cc b/chrome/browser/chrome_content_browser_client_platform_browsertest.cc
new file mode 100644
index 0000000..f183e34a
--- /dev/null
+++ b/chrome/browser/chrome_content_browser_client_platform_browsertest.cc
@@ -0,0 +1,69 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/platform_browser_test.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+class ChromeContentBrowserClientPlatformBrowserTest
+    : public PlatformBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    PlatformBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  content::WebContents* web_contents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+};
+
+// window.open() with the 'background' window feature from ordinary web content
+// must not be allowed to open a new window, even with transient user
+// activation.
+IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientPlatformBrowserTest,
+                       BackgroundFeatureFromOrdinaryWebContentIsBlocked) {
+  ASSERT_TRUE(content::NavigateToURL(
+      web_contents(), embedded_test_server()->GetURL("/title1.html")));
+
+  const size_t initial_web_contents_count = content::GetAllWebContents().size();
+
+  // 1. Attempt to open a window with the 'background' feature.
+  // Using default EvalJs options executes with user_gesture = true, which is
+  // critical: without a user gesture, the standard popup blocker would reject
+  // the call before reaching ChromeContentBrowserClient::CanCreateWindow().
+  // Even with user activation, ordinary web content lacks extension background
+  // permissions and must be refused when requesting 'background' window
+  // features.
+  EXPECT_EQ(true,
+            content::EvalJs(
+                web_contents(),
+                "window.open('/title2.html', '', 'background') === null"));
+
+  // Verify browser-side state: no new WebContents or tab was created.
+  EXPECT_EQ(initial_web_contents_count, content::GetAllWebContents().size());
+
+  // 2. Control assertion: verify that an ordinary popup without 'background'
+  // succeeds with transient user activation, proving that the failure above
+  // was specifically due to the 'background' window feature and not because
+  // popups are globally disabled in the test environment.
+  EXPECT_EQ(true,
+            content::EvalJs(web_contents(),
+                            "window.open('/title2.html', '', '') !== null"));
+
+  // Verify that exactly one new WebContents was created by the control popup.
+  EXPECT_EQ(initial_web_contents_count + 1,
+            content::GetAllWebContents().size());
+}
+
+}  // namespace
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c56e0ea..24995b9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1021,6 +1021,7 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
   sources = [
+    "../browser/chrome_content_browser_client_platform_browsertest.cc",
     "../browser/loadtimes_extension_bindings_browsertest.cc",
     "../browser/no_best_effort_tasks_browsertest.cc",
     "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/chrome_content_browser_client_platform_browsertest.cc b/chrome/browser/chrome_content_browser_client_platform_browsertest.cc
new file mode 100644
index 0000000..f183e34a
--- /dev/null
+++ b/chrome/browser/chrome_content_browser_client_platform_browsertest.cc
@@ -0,0 +1,69 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/platform_browser_test.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+class ChromeContentBrowserClientPlatformBrowserTest
+    : public PlatformBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    PlatformBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  content::WebContents* web_contents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+};
+
+// window.open() with the 'background' window feature from ordinary web content
+// must not be allowed to open a new window, even with transient user
+// activation.
+IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientPlatformBrowserTest,
+                       BackgroundFeatureFromOrdinaryWebContentIsBlocked) {
+  ASSERT_TRUE(content::NavigateToURL(
+      web_contents(), embedded_test_server()->GetURL("/title1.html")));
+
+  const size_t initial_web_contents_count = content::GetAllWebContents().size();
+
+  // 1. Attempt to open a window with the 'background' feature.
+  // Using default EvalJs options executes with user_gesture = true, which is
+  // critical: without a user gesture, the standard popup blocker would reject
+  // the call before reaching ChromeContentBrowserClient::CanCreateWindow().
+  // Even with user activation, ordinary web content lacks extension background
+  // permissions and must be refused when requesting 'background' window
+  // features.
+  EXPECT_EQ(true,
+            content::EvalJs(
+                web_contents(),
+                "window.open('/title2.html', '', 'background') === null"));
+
+  // Verify browser-side state: no new WebContents or tab was created.
+  EXPECT_EQ(initial_web_contents_count, content::GetAllWebContents().size());
+
+  // 2. Control assertion: verify that an ordinary popup without 'background'
+  // succeeds with transient user activation, proving that the failure above
+  // was specifically due to the 'background' window feature and not because
+  // popups are globally disabled in the test environment.
+  EXPECT_EQ(true,
+            content::EvalJs(web_contents(),
+                            "window.open('/title2.html', '', '') !== null"));
+
+  // Verify that exactly one new WebContents was created by the control popup.
+  EXPECT_EQ(initial_web_contents_count + 1,
+            content::GetAllWebContents().size());
+}
+
+}  // namespace
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c56e0ea..24995b9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1021,6 +1021,7 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
   sources = [
+    "../browser/chrome_content_browser_client_platform_browsertest.cc",
     "../browser/loadtimes_extension_bindings_browsertest.cc",
     "../browser/no_best_effort_tasks_browsertest.cc",
     "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential popup blocker bypass on Chrome for Android via window.open background feature

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential vulnerability in Chrome for Android allows arbitrary web pages to bypass the popup blocker and programmatically open new tabs without user activation. This occurs because the extension-only checks inside the browser client are compiled out on Android, leaving an unconditional early return when handling the ‘background’ window container type. Consequently, the popup blocking logic is bypassed, allowing automated popup spam or tab creation.

Affected files:

  • chrome/browser/chrome_content_browser_client.cc
  • third_party/blink/renderer/core/page/create_window.cc
  • content/renderer/render_frame_impl.cc
  • content/browser/renderer_host/ipc_utils.cc
  • components/embedder_support/android/delegate/web_contents_delegate_android.cc

Estimated timestamp from git blame: 2014-07-16

Potential Popup Blocker Bypass on Chrome for Android via window.open('background')

Summary

We have identified a potential security vulnerability in Chrome for Android where the browser’s popup blocker can be bypassed without requiring a compromised renderer or user activation. By calling window.open(url, '_blank', 'background'), any web page can programmatically open new tabs, leading to potential popup spam, clickjacking, and phishing abuse.

Vulnerability Mechanics

When a page calls window.open(..., 'background'), the window opening flow executes as follows:

  1. Feature Parsing: In third_party/blink/renderer/core/page/create_window.cc around line 210, the renderer unconditionally parses the 'background' keyword and sets window_features.background = true.
  2. Parameters Mapping: In content/renderer/render_frame_impl.cc:6862, WindowFeaturesToContainerType maps this feature to content::mojom::WindowContainerType::BACKGROUND inside params->window_container_type.
  3. IPC Transfer: The renderer sends these parameters via the synchronous CreateNewWindow IPC interface to the browser process (content/browser/renderer_host/render_frame_host_impl.cc:10274).
  4. Browser Policy Evaluation: The browser process delegates the request validation to the content embedder’s CanCreateWindow interface. In chrome/browser/chrome_content_browser_client.cc around lines 4529–4553:
    if (container_type == content::mojom::WindowContainerType::BACKGROUND) {
    #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
        // Extension-specific background permission checks
        ...
    #endif
        return true;
    }
    
    Because Chrome for Android compiles without extension support (ENABLE_EXTENSIONS_CORE is false), the compiler strips the permission check inside the #if block. This results in an unconditional early return true; for any page requesting a BACKGROUND container type.
  5. Blocker Bypass: Due to this early return, the execution path never reaches the end of the CanCreateWindow function where ConsiderForPopupBlocking and MaybeBlockPopup are invoked to block un-activated window creations. As a result, the request is unconditionally approved and processed as a normal tab creation on Android.

Potential Attack Vector

An attacker could potentially trigger this bypass from any untrusted web context by serving a page with the following snippet:

<script>
  // Potential trigger vector: opens a background tab without user gesture
  window.open('https://example.com/', '_blank', 'background');
</script>

Note: These steps represent a potential trigger flow. Our tooling does not currently have the capacity to dynamically execute and verify the POC on Chrome for Android.

We suggest the following structural changes to resolve this potential issue:

  1. Move the return true; statement at chrome/browser/chrome_content_browser_client.cc:4552 inside the #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) block. In environments without extensions support, the handler should fall through to standard popup blocking validation.
  2. In content/browser/renderer_host/ipc_utils.cc:VerifyCreateNewWindowParams, add defense-in-depth checks to validate and reject requests specifying WindowContainerType::BACKGROUND or PERSISTENT from non-privileged/non-extension renderer processes.

Evaluated with Chrome root at commit: d8b226a3be7c9c1ac9240c09e14698866c82e4ac


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker