CVE-2026-17883
Overview
Files Changed
chrome/browser/headless/test/headless_mode_protocol_browsertest.cccomponents/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txtcomponents/headless/test/data/protocol/shared/window-open-click-opener-id.jscomponents/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txtcomponents/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js
Patch
From 7bf55f487410fdd8df3420aaafdb349e44ab44ab Mon Sep 17 00:00:00 2001 From: Peter Kvitek <[email protected]> Date: Thu, 25 Jun 2026 09:26:58 -0700 Subject: [PATCH] [headless] Defer WebContents opener handling to //content http://crrev.com/c/7944804 added opener id propagation to headless shell, however, it should not be specified for Shift+Click navigation. This CL fixes this by passing the source SiteInstance to WebContents::Create() and removing manual opener RenderFrameHost tracking from HeadlessWebContents::Builder. Also: - Convert C++ opener suppression and BlockNewWebContents tests to protocol JS tests (window-open-noopener-click-opener-id.js and block-new-web-contents.js). - Add window-open-click-opener-id.js protocol test. - Remove obsolete open-url-sandbox-privileges.js sanity test. Bug: 523639090 Change-Id: I73d41c3c584b9f17d9c9e62cf8d31f01b1016c3e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7987972 Reviewed-by: Andrey Kosyakov <[email protected]> Commit-Queue: Peter Kvitek <[email protected]> Cr-Commit-Position: refs/heads/main@{#1652483} --- diff --git a/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc index ce8037ef..5ba7b37 100644 --- a/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc +++ b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc @@ -393,6 +393,15 @@ HEADLESS_MODE_PROTOCOL_TEST(WindowOpenOnSecondaryScreen, "shared/window-open-on-secondary-screen.js") +HEADLESS_MODE_PROTOCOL_TEST(WindowOpenClickOpenerId, + "shared/window-open-click-opener-id.js") + +HEADLESS_MODE_PROTOCOL_TEST(WindowOpenNoopenerClickOpenerId, + "shared/window-open-noopener-click-opener-id.js") + +HEADLESS_MODE_PROTOCOL_TEST(WindowOpenShiftClickOpenerId, + "shared/window-open-shift-click-opener-id.js") + // TODO(crbug.com/40283476): CreateTargetSecondaryScreen is failing on Mac #if !BUILDFLAG(IS_MAC) #define MAYBE_CreateTargetSecondaryScreen CreateTargetSecondaryScreen diff --git a/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt b/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt new file mode 100644 index 0000000..0ea92c7 --- /dev/null +++ b/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt @@ -0,0 +1,2 @@ +Tests that opener IS specified on a page opened via Click. +TargetInfo.openerId: initialTargetId \ No newline at end of file diff --git a/components/headless/test/data/protocol/shared/window-open-click-opener-id.js b/components/headless/test/data/protocol/shared/window-open-click-opener-id.js new file mode 100644 index 0000000..fa47747 --- /dev/null +++ b/components/headless/test/data/protocol/shared/window-open-click-opener-id.js @@ -0,0 +1,72 @@ +// 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. +// +// META: --disable-popup-blocking + +(async function(testRunner) { + const {session, dp} = await testRunner.startBlank( + 'Tests that opener IS specified on a page opened via Click.'); + + const {sessionId} = + (await testRunner.browserP().Target.attachToBrowserTarget({})).result; + const bp = (new TestRunner.Session(testRunner, sessionId)).protocol; + + const targetInfoResponse = await dp.Target.getTargetInfo(); + const initialTargetId = + (targetInfoResponse.result || targetInfoResponse).targetInfo.targetId; + + await bp.Target.setAutoAttach( + {autoAttach: true, waitForDebuggerOnStart: false, flatten: true}); + + const targetAttachedPromise = new Promise(resolve => { + bp.Target.onAttachedToTarget(event => { + const targetInfo = event.params.targetInfo; + if (targetInfo.type === 'page' && + targetInfo.targetId !== initialTargetId) { + resolve(targetInfo); + } + }); + }); + + const HttpInterceptor = + await testRunner.loadScriptAbsolute('../resources/http-interceptor.js'); + const httpInterceptor = await (new HttpInterceptor(testRunner, bp)).init(); + httpInterceptor.setDisableRequestedUrlsLogging(true); + + httpInterceptor.addResponse('https://example.com/index.html', ` + <html> + <body style="margin: 0; padding: 0;"> + <a href="https://example.com/page2.html" target="_blank" + style="display: block; width: 100px; height: 100px;">Click</a> + </body> + </html> + `); + httpInterceptor.addResponse('https://example.com/page2.html', ` + <html><body>Page 2</body></html> + `); + + await session.navigate('https://example.com/index.html'); + + await dp.Input.dispatchMouseEvent({ + type: 'mousePressed', + x: 50, + y: 50, + button: 'left', + clickCount: 1, + }); + await dp.Input.dispatchMouseEvent({ + type: 'mouseReleased', + x: 50, + y: 50, + button: 'left', + clickCount: 1, + }); + + const newTargetInfo = await targetAttachedPromise; + testRunner.log(`TargetInfo.openerId: ${ + newTargetInfo.openerId === initialTargetId ? 'initialTargetId' : + newTargetInfo.openerId}`); + + testRunner.completeTest(); +}); diff --git a/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt new file mode 100644 index 0000000..e032032 --- /dev/null +++ b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt @@ -0,0 +1,2 @@ +Tests that opener is NOT specified on a page opened via Click with rel=noopener. +PASS \ No newline at end of file diff --git a/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js new file mode 100644 index 0000000..2889911 --- /dev/null +++ b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js @@ -0,0 +1,77 @@ +// 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. +// +// META: --disable-popup-blocking + +(async function(testRunner) { + const {session, dp} = await testRunner.startBlank( + 'Tests that opener is NOT specified on a page opened ' + + 'via Click with rel=noopener.'); + + const {sessionId} = + (await testRunner.browserP().Target.attachToBrowserTarget({})).result; + const bp = (new TestRunner.Session(testRunner, sessionId)).protocol; + + const targetInfoResponse = await dp.Target.getTargetInfo(); + const initialTargetId = + (targetInfoResponse.result || targetInfoResponse).targetInfo.targetId; + + await bp.Target.setAutoAttach( + {autoAttach: true, waitForDebuggerOnStart: false, flatten: true}); + + const targetAttachedPromise = new Promise(resolve => { + bp.Target.onAttachedToTarget(event => { + const targetInfo = event.params.targetInfo; + if (targetInfo.type === 'page' && + targetInfo.targetId !== initialTargetId) { + resolve(event.params); + } + }); + }); + + const HttpInterceptor = + await testRunner.loadScriptAbsolute('../resources/http-interceptor.js'); + const httpInterceptor = await (new HttpInterceptor(testRunner, bp)).init(); + httpInterceptor.setDisableRequestedUrlsLogging(true); + + httpInterceptor.addResponse('https://example.com/index.html', ` + <html> + <body style="margin: 0; padding: 0;"> + <a href="https://example.com/page2.html" target="_blank" rel="noopener" + style="display: block; width: 100px; height: 100px;">Click</a> + </body> + </html> + `); + httpInterceptor.addResponse('https://example.com/page2.html', ` + <html><body>Page 2</body></html> + `); + + await session.navigate('https://example.com/index.html'); +
Regression Test / PoC
diff --git a/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
index ce8037ef..5ba7b37 100644
--- a/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
+++ b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
@@ -393,6 +393,15 @@
HEADLESS_MODE_PROTOCOL_TEST(WindowOpenOnSecondaryScreen,
"shared/window-open-on-secondary-screen.js")
+HEADLESS_MODE_PROTOCOL_TEST(WindowOpenClickOpenerId,
+ "shared/window-open-click-opener-id.js")
+
+HEADLESS_MODE_PROTOCOL_TEST(WindowOpenNoopenerClickOpenerId,
+ "shared/window-open-noopener-click-opener-id.js")
+
+HEADLESS_MODE_PROTOCOL_TEST(WindowOpenShiftClickOpenerId,
+ "shared/window-open-shift-click-opener-id.js")
+
// TODO(crbug.com/40283476): CreateTargetSecondaryScreen is failing on Mac
#if !BUILDFLAG(IS_MAC)
#define MAYBE_CreateTargetSecondaryScreen CreateTargetSecondaryScreen
diff --git a/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt b/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt
new file mode 100644
index 0000000..0ea92c7
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-click-opener-id-expected.txt
@@ -0,0 +1,2 @@
+Tests that opener IS specified on a page opened via Click.
+TargetInfo.openerId: initialTargetId
\ No newline at end of file
diff --git a/components/headless/test/data/protocol/shared/window-open-click-opener-id.js b/components/headless/test/data/protocol/shared/window-open-click-opener-id.js
new file mode 100644
index 0000000..fa47747
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-click-opener-id.js
@@ -0,0 +1,72 @@
+// 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.
+//
+// META: --disable-popup-blocking
+
+(async function(testRunner) {
+ const {session, dp} = await testRunner.startBlank(
+ 'Tests that opener IS specified on a page opened via Click.');
+
+ const {sessionId} =
+ (await testRunner.browserP().Target.attachToBrowserTarget({})).result;
+ const bp = (new TestRunner.Session(testRunner, sessionId)).protocol;
+
+ const targetInfoResponse = await dp.Target.getTargetInfo();
+ const initialTargetId =
+ (targetInfoResponse.result || targetInfoResponse).targetInfo.targetId;
+
+ await bp.Target.setAutoAttach(
+ {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
+
+ const targetAttachedPromise = new Promise(resolve => {
+ bp.Target.onAttachedToTarget(event => {
+ const targetInfo = event.params.targetInfo;
+ if (targetInfo.type === 'page' &&
+ targetInfo.targetId !== initialTargetId) {
+ resolve(targetInfo);
+ }
+ });
+ });
+
+ const HttpInterceptor =
+ await testRunner.loadScriptAbsolute('../resources/http-interceptor.js');
+ const httpInterceptor = await (new HttpInterceptor(testRunner, bp)).init();
+ httpInterceptor.setDisableRequestedUrlsLogging(true);
+
+ httpInterceptor.addResponse('https://example.com/index.html', `
+ <html>
+ <body style="margin: 0; padding: 0;">
+ <a href="https://example.com/page2.html" target="_blank"
+ style="display: block; width: 100px; height: 100px;">Click</a>
+ </body>
+ </html>
+ `);
+ httpInterceptor.addResponse('https://example.com/page2.html', `
+ <html><body>Page 2</body></html>
+ `);
+
+ await session.navigate('https://example.com/index.html');
+
+ await dp.Input.dispatchMouseEvent({
+ type: 'mousePressed',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ });
+ await dp.Input.dispatchMouseEvent({
+ type: 'mouseReleased',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ });
+
+ const newTargetInfo = await targetAttachedPromise;
+ testRunner.log(`TargetInfo.openerId: ${
+ newTargetInfo.openerId === initialTargetId ? 'initialTargetId' :
+ newTargetInfo.openerId}`);
+
+ testRunner.completeTest();
+});
diff --git a/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt
new file mode 100644
index 0000000..e032032
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id-expected.txt
@@ -0,0 +1,2 @@
+Tests that opener is NOT specified on a page opened via Click with rel=noopener.
+PASS
\ No newline at end of file
diff --git a/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js
new file mode 100644
index 0000000..2889911
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-noopener-click-opener-id.js
@@ -0,0 +1,77 @@
+// 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.
+//
+// META: --disable-popup-blocking
+
+(async function(testRunner) {
+ const {session, dp} = await testRunner.startBlank(
+ 'Tests that opener is NOT specified on a page opened ' +
+ 'via Click with rel=noopener.');
+
+ const {sessionId} =
+ (await testRunner.browserP().Target.attachToBrowserTarget({})).result;
+ const bp = (new TestRunner.Session(testRunner, sessionId)).protocol;
+
+ const targetInfoResponse = await dp.Target.getTargetInfo();
+ const initialTargetId =
+ (targetInfoResponse.result || targetInfoResponse).targetInfo.targetId;
+
+ await bp.Target.setAutoAttach(
+ {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
+
+ const targetAttachedPromise = new Promise(resolve => {
+ bp.Target.onAttachedToTarget(event => {
+ const targetInfo = event.params.targetInfo;
+ if (targetInfo.type === 'page' &&
+ targetInfo.targetId !== initialTargetId) {
+ resolve(event.params);
+ }
+ });
+ });
+
+ const HttpInterceptor =
+ await testRunner.loadScriptAbsolute('../resources/http-interceptor.js');
+ const httpInterceptor = await (new HttpInterceptor(testRunner, bp)).init();
+ httpInterceptor.setDisableRequestedUrlsLogging(true);
+
+ httpInterceptor.addResponse('https://example.com/index.html', `
+ <html>
+ <body style="margin: 0; padding: 0;">
+ <a href="https://example.com/page2.html" target="_blank" rel="noopener"
+ style="display: block; width: 100px; height: 100px;">Click</a>
+ </body>
+ </html>
+ `);
+ httpInterceptor.addResponse('https://example.com/page2.html', `
+ <html><body>Page 2</body></html>
+ `);
+
+ await session.navigate('https://example.com/index.html');
+
+ await dp.Input.dispatchMouseEvent({
+ type: 'mousePressed',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ });
+ await dp.Input.dispatchMouseEvent({
+ type: 'mouseReleased',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ });
+
+ const {sessionId: childSessionId} = await targetAttachedPromise;
+ const childSession = new TestRunner.Session(testRunner, childSessionId);
+ const hasOpener = await childSession.evaluate('window.opener !== null');
+ if (!hasOpener) {
+ testRunner.log('PASS');
+ } else {
+ testRunner.log('FAIL: window.opener is not null');
+ }
+
+ testRunner.completeTest();
+});
diff --git a/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id-expected.txt b/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id-expected.txt
new file mode 100644
index 0000000..c817003
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id-expected.txt
@@ -0,0 +1,2 @@
+Tests that opener is NOT specified on a page opened via Shift+Click.
+TargetInfo.openerId: undefined
\ No newline at end of file
diff --git a/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id.js b/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id.js
new file mode 100644
index 0000000..8be6cf20
--- /dev/null
+++ b/components/headless/test/data/protocol/shared/window-open-shift-click-opener-id.js
@@ -0,0 +1,72 @@
+// 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.
+//
+// META: --disable-popup-blocking
+
+(async function(testRunner) {
+ const {session, dp} = await testRunner.startBlank(
+ 'Tests that opener is NOT specified on a page opened via Shift+Click.');
+
+ const {sessionId} =
+ (await testRunner.browserP().Target.attachToBrowserTarget({})).result;
+ const bp = (new TestRunner.Session(testRunner, sessionId)).protocol;
+
+ const targetInfoResponse = await dp.Target.getTargetInfo();
+ const initialTargetId =
+ (targetInfoResponse.result || targetInfoResponse).targetInfo.targetId;
+
+ await bp.Target.setAutoAttach(
+ {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
+
+ const targetAttachedPromise = new Promise(resolve => {
+ bp.Target.onAttachedToTarget(event => {
+ const targetInfo = event.params.targetInfo;
+ if (targetInfo.type === 'page' &&
+ targetInfo.targetId !== initialTargetId) {
+ resolve(targetInfo);
+ }
+ });
+ });
+
+ const HttpInterceptor =
+ await testRunner.loadScriptAbsolute('../resources/http-interceptor.js');
+ const httpInterceptor = await (new HttpInterceptor(testRunner, bp)).init();
+ httpInterceptor.setDisableRequestedUrlsLogging(true);
+
+ httpInterceptor.addResponse('https://example.com/index.html', `
+ <html>
+ <body style="margin: 0; padding: 0;">
+ <a href="https://example.com/page2.html" target="_blank"
+ style="display: block; width: 100px; height: 100px;">Click</a>
+ </body>
+ </html>
+ `);
+ httpInterceptor.addResponse('https://example.com/page2.html', `
+ <html><body>Page 2</body></html>
+ `);
+
+ await session.navigate('https://example.com/index.html');
+
+ await dp.Input.dispatchMouseEvent({
+ type: 'mousePressed',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ modifiers: 8, // 8 = Shift modifier
+ });
+ await dp.Input.dispatchMouseEvent({
+ type: 'mouseReleased',
+ x: 50,
+ y: 50,
+ button: 'left',
+ clickCount: 1,
+ modifiers: 8, // 8 = Shift modifier
+ });
+
+ const newTargetInfo = await targetAttachedPromise;
+ testRunner.log(`TargetInfo.openerId: ${newTargetInfo.openerId}`);
+
+ testRunner.completeTest();
+});
diff --git a/headless/test/data/protocol/sanity/block-new-web-contents-expected.txt b/headless/test/data/protocol/sanity/block-new-web-contents-expected.txt
new file mode 100644
index 0000000..6ca8945a
--- /dev/null
+++ b/headless/test/data/protocol/sanity/block-new-web-contents-expected.txt
@@ -0,0 +1,3 @@
+Tests that --block-new-web-contents blocks new web contents creation.
+window.open() result: null
+New page targets count: 0
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/block-new-web-contents.js b/headless/test/data/protocol/sanity/block-new-web-contents.js
new file mode 100644
index 0000000..f0f1ef5
--- /dev/null
... (truncated)
Original Bug Report
Sandbox escape in Headless Chrome via OpenURLFromTab dropping the opener
Flapjack, 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: Headless Chrome fails to propagate sandbox flags when a navigation opens a new tab via OpenURLFromTab. An attacker in a sandboxed iframe with allow-popups can use a synthetic middle-click to trigger this path, bypassing the sandbox restrictions and the headless popup blocker.
Affected files:
headless/lib/browser/headless_web_contents_impl.cc
Estimated timestamp from git blame: Unknown (Google3 checkout)
Description
In Headless Chrome, the HeadlessWebContentsImpl::Delegate::OpenURLFromTab method handles navigations that open in a new tab or window (e.g., via a middle-click). When processing NEW_FOREGROUND_TAB or NEW_BACKGROUND_TAB dispositions, the method creates a new WebContents instance using CreateWebContentsBuilder().Build().
However, this creation path fails to provide an opener reference to the WebContents::CreateParams. In Chromium’s content layer, sandbox flags and security policies are inherited from the opener during the creation of a new WebContents (specifically in WebContentsImpl::CreateWithOpener). Because the opener IDs are left uninitialized (defaulting to kInvalidUniqueID and kRoutingIdNone), the new window is created with a default, unrestricted security context. It fails to inherit the sandbox restrictions of the initiating frame.
Potential Attack Steps
(Note: These are suggested steps; our tooling agent does not yet have the ability to run code to verify them dynamically.)
- An attacker embeds a sandboxed iframe in a page loaded by Headless Chrome. The iframe has
sandbox="allow-scripts allow-popups"(notably lackingallow-popups-to-escape-sandbox). - Inside the iframe, the attacker executes JavaScript to create an anchor (
<a>) and dispatches a synthetic middle-click event on it:a.dispatchEvent(new MouseEvent('click', {button: 1})). - The Blink renderer evaluates the middle-click as an untrusted event targeting a new tab. Because
allow-popupsis present in the sandbox flags, Blink allows the untrusted event to proceed (FrameLoader::StartNavigation) and routes it toRenderFrameImpl::OpenURL. - The browser process receives the IPC and invokes
HeadlessWebContentsImpl::Delegate::OpenURLFromTabwith aNEW_FOREGROUND_TABorNEW_BACKGROUND_TABdisposition. - The new
WebContentsis created viaHeadlessWebContentsImpl::Create, missing theopenermetadata. - The new tab loads the attacker’s URL without the sandbox restrictions of the initiating iframe, granting full privileges of the origin.
This attack vector is highly reliable in Headless Chrome because it lacks the standard popup blocker that would typically block synthetic popup events in regular Chrome. Furthermore, this specific code path completely bypasses the Headless block_new_web_contents() setting, which embedders rely on to prevent untrusted popups.
Vulnerable Code
In headless/lib/browser/headless_web_contents_impl.cc, the OpenURLFromTab implementation creates the new contents without specifying an opener:
case WindowOpenDisposition::NEW_POPUP:
case WindowOpenDisposition::NEW_WINDOW:
case WindowOpenDisposition::NEW_BACKGROUND_TAB:
case WindowOpenDisposition::NEW_FOREGROUND_TAB: {
HeadlessWebContentsImpl* child_contents = HeadlessWebContentsImpl::From(
headless_web_contents_->browser_context()
->CreateWebContentsBuilder()
.SetWindowBounds(source->GetContainerBounds())
.Build());
target = child_contents->web_contents();
break;
}
The Build() call invokes HeadlessWebContentsImpl::Create, which initializes content::WebContents::CreateParams without the opener reference:
std::unique_ptr<HeadlessWebContentsImpl> HeadlessWebContentsImpl::Create(
HeadlessWebContents::Builder* builder) {
content::WebContents::CreateParams create_params(builder->browser_context_);
auto headless_web_contents = base::WrapUnique(
new HeadlessWebContentsImpl(content::WebContents::Create(create_params)));
// ...
}
Suggested Fix
- Update
HeadlessWebContents::Builderto accept an optional openercontent::WebContents*reference. - In
HeadlessWebContentsImpl::Create, use the provided opener reference to set theopener_render_process_id,opener_render_frame_id, andopener_suppressedfields incontent::WebContents::CreateParams. - In
HeadlessWebContentsImpl::Delegate::OpenURLFromTab, pass thesourceWebContentsas the opener to the builder before callingBuild().
Evaluated with Chrome root at commit: 65b3256311f3ab6fb9870eaa522de7e6dd2663bb
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.