Chrome · DevTools
CVE-2026-17786
Logic Error in DevTools
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
RegisteredExtensionfront_end/panels/common/ExtensionServer.ts |
modified | |
constructorfront_end/panels/common/ExtensionServer.ts |
modified | |
iffront_end/panels/common/ExtensionServer.ts |
modified |
Files Changed
front_end/core/host/InspectorFrontendHostAPI.tsfront_end/core/host/UserMetrics.tsfront_end/devtools_compatibility.jsfront_end/panels/common/ExtensionServer.test.tsfront_end/panels/common/ExtensionServer.ts
Patch
From 951c1e9f60ad86eb0a6b247b6cdf8b2f6e3f301d Mon Sep 17 00:00:00 2001 From: Philip Pfaffe <[email protected]> Date: Tue, 23 Jun 2026 10:36:05 +0000 Subject: [PATCH] Move chrome-extension access check out of eval Fixed: 513770449 Change-Id: Iebc4ba6f5dfc9804ffd456d9c2756d2ec0cd6e5a Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7984249 Reviewed-by: Danil Somsikov <[email protected]> Commit-Queue: Philip Pfaffe <[email protected]> --- diff --git a/front_end/core/host/InspectorFrontendHostAPI.ts b/front_end/core/host/InspectorFrontendHostAPI.ts index 627f62d..03a0a11 100644 --- a/front_end/core/host/InspectorFrontendHostAPI.ts +++ b/front_end/core/host/InspectorFrontendHostAPI.ts @@ -556,6 +556,5 @@ LighthouseCategoryUsed = 'DevTools.LighthouseCategoryUsed', SwatchActivated = 'DevTools.SwatchActivated', BuiltInAiAvailability = 'DevTools.BuiltInAiAvailability', - ExtensionEvalTarget = 'DevTools.ExtensionEvalTarget', // LINT.ThenChange(/front_end/devtools_compatibility.js:EnumeratedHistogram) } diff --git a/front_end/core/host/UserMetrics.ts b/front_end/core/host/UserMetrics.ts index 59f17c8..882fcee 100644 --- a/front_end/core/host/UserMetrics.ts +++ b/front_end/core/host/UserMetrics.ts @@ -254,10 +254,6 @@ 'DevTools.Insights.ShortTeaserGenerationTime', timeInMilliseconds); } - extensionEvalTarget(target: ExtensionEvalTarget): void { - InspectorFrontendHostInstance.recordEnumeratedHistogram( - EnumeratedHistogram.ExtensionEvalTarget, target, ExtensionEvalTarget.MAX_VALUE); - } } /** @@ -1197,10 +1193,3 @@ DISABLED_NO_GPU = 9, MAX_VALUE = 10, } - -export const enum ExtensionEvalTarget { - WEB_PAGE = 0, - SAME_EXTENSION = 1, - OTHER_EXTENSION = 2, - MAX_VALUE = 3, -} diff --git a/front_end/devtools_compatibility.js b/front_end/devtools_compatibility.js index 68e99f9..894969c 100644 --- a/front_end/devtools_compatibility.js +++ b/front_end/devtools_compatibility.js @@ -444,8 +444,7 @@ TimelineNavigationSettingState: 'DevTools.TimelineNavigationSettingState', SyncSetting: 'DevTools.SyncSetting', SwatchActivated: 'DevTools.SwatchActivated', - BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability', - ExtensionEvalTarget: 'DevTools.ExtensionEvalTarget' + BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability' // LINT.ThenChange(/front_end/core/host/InspectorFrontendHostAPI.ts:EnumeratedHistogram) }; diff --git a/front_end/panels/common/ExtensionServer.test.ts b/front_end/panels/common/ExtensionServer.test.ts index 85fc825..e30d14d 100644 --- a/front_end/panels/common/ExtensionServer.test.ts +++ b/front_end/panels/common/ExtensionServer.test.ts @@ -808,6 +808,36 @@ assert.deepEqual(result.error?.details, ['Permission denied']); }); + it('blocks evaluation on other extension execution contexts', async () => { + assert.isUndefined(context.chrome.devtools); + + const parentFrameUrl = allowedUrl; + const parentFrame = await setUpFrame('parent', parentFrameUrl, undefined, parentFrameUrl); + + // Yield to the microtask queue to allow the extension to be initialized. + await new Promise(r => setTimeout(r, 0)); + + // Create a non-default context with a different extension origin. + const otherExtensionOrigin = urlString`chrome-extension://other-extension`; + const runtimeModel = parentFrame.resourceTreeModel()?.target().model(SDK.RuntimeModel.RuntimeModel); + assert.exists(runtimeModel); + runtimeModel.executionContextCreated({ + id: 1 as Protocol.Runtime.ExecutionContextId, + origin: otherExtensionOrigin, + name: otherExtensionOrigin, + uniqueId: otherExtensionOrigin, + auxData: {frameId: parentFrame.id, isDefault: false}, + }); + const result = await new Promise<{result: unknown, error?: {details: unknown[]}}>( + r => context.chrome.devtools?.inspectedWindow.eval( + // The typings don't match the implementation, so we need to cast to any here to make ts happy. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + '4', {frameURL: parentFrameUrl, scriptExecutionContext: otherExtensionOrigin} as any, + (result, error) => r({result, error}))); + + assert.deepEqual(result.error?.details, ['Permission denied']); + }); + it('blocks evaluation on blocked sub-executioncontexts', async () => { assert.isUndefined(context.chrome.devtools); diff --git a/front_end/panels/common/ExtensionServer.ts b/front_end/panels/common/ExtensionServer.ts index ec2342a..fb7ea53 100644 --- a/front_end/panels/common/ExtensionServer.ts +++ b/front_end/panels/common/ExtensionServer.ts @@ -85,7 +85,8 @@ } class RegisteredExtension { - constructor(readonly name: string, readonly hostsPolicy: HostsPolicy, readonly allowFileAccess: boolean) { + constructor(readonly origin: string, readonly name: string, readonly hostsPolicy: HostsPolicy, + readonly allowFileAccess: boolean) { } isAllowedOnTarget(inspectedURL?: Platform.DevToolsPath.UrlString): boolean { @@ -104,6 +105,14 @@ return false; } + if (parsedURL.protocol === 'chrome-extension:') { + if (parsedURL.origin !== this.origin) { + if (!Root.Runtime.hostConfig.extensionsOnChromeUrls?.enabled) { + return false; + } + } + } + if (!ExtensionServer.canInspectURL(inspectedURL)) { return false; } @@ -1388,7 +1397,8 @@ const startPageURL = new URL((startPage)); const extensionOrigin = startPageURL.origin; const name = extensionInfo.name || `Extension ${extensionOrigin}`; - const extensionRegistration = new RegisteredExtension(name, hostsPolicy, Boolean(extensionInfo.allowFileAccess)); + const extensionRegistration = + new RegisteredExtension(extensionOrigin, name, hostsPolicy, Boolean(extensionInfo.allowFileAccess)); if (!extensionRegistration.isAllowedOnTarget(inspectedURL)) { this.#pendingExtensions.push(extensionInfo); return; @@ -1609,25 +1619,6 @@ return this.status.E_FAILED('Permission denied'); } - try { - const parsedUrl = new URL(frame.url); - let targetType = Host.UserMetrics.ExtensionEvalTarget.WEB_PAGE; - if (parsedUrl.protocol === 'chrome-extension:') { - if (parsedUrl.origin === securityOrigin) { - targetType = Host.UserMetrics.ExtensionEvalTarget.SAME_EXTENSION; - } else { - targetType = Host.UserMetrics.ExtensionEvalTarget.OTHER_EXTENSION; - if (!Root.Runtime.hostConfig.extensionsOnChromeUrls?.enabled) { - return this.status.E_FAILED( - 'Access to extension URLs is restricted; use --extensions-on-chrome-urls to enable.'); - } - } - } - Host.userMetrics.extensionEvalTarget(targetType); - } catch { - // Ignore invalid URLs. - } - void context .evaluate( {
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