Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in DevTools
DescriptionInappropriate implementation in DevTools
ComponentDevTools
Bug ClassLogic Error
Tracker513864014
Fix commit88681798359e (devtools/devtools-frontend) +59/-11
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
front_end/ui/helpers/OpenInNewTab.ts
modified
if
front_end/ui/kit/link/Link.ts
modified

Files Changed

  • front_end/panels/application/ServiceWorkersView.ts
  • front_end/panels/application/components/BackForwardCacheView.ts
  • front_end/panels/settings/components/SyncSection.ts
  • front_end/ui/helpers/OpenInNewTab.test.ts
  • front_end/ui/helpers/OpenInNewTab.ts
  • front_end/ui/kit/link/Link.test.ts
  • front_end/ui/kit/link/Link.ts
From 88681798359ea08d46cb38e1db36f177ef98ef96 Mon Sep 17 00:00:00 2001
From: Danil Somsikov <dsv@chromium.org>
Date: Thu, 11 Jun 2026 03:23:42 -0700
Subject: [PATCH] Introduce allowPrivileged option for opening links

This change adds an `allowPrivileged` option to `UIHelpers.openInNewTab` and the `Link` component. When true, this option permits navigating to `chrome://` URLs using CDP's `TargetAgent.invoke_createTarget`. Without this option, `chrome://` links will be opened via `InspectorFrontendHost.openInNewTab`.

Existing instances of `chrome://` links that should be opened via CDP have been updated to set `allowPrivileged` to true.

Bug: 513866380, 513768645, 513735900, 513864014, 513781245, 513838421
Change-Id: Ibbf8a0ed1733e96d8144faf5a6c83b63e38a0063
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7921415
Auto-Submit: Danil Somsikov <dsv@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
---

diff --git a/front_end/panels/application/ServiceWorkersView.ts b/front_end/panels/application/ServiceWorkersView.ts
index f81f8f8..fec4a42 100644
--- a/front_end/panels/application/ServiceWorkersView.ts
+++ b/front_end/panels/application/ServiceWorkersView.ts
@@ -228,8 +228,8 @@
     othersView.show(othersDiv);
     const othersSection = othersView.appendSection(i18nString(UIStrings.serviceWorkersFromOtherOrigins));
     const othersSectionRow = othersSection.appendRow();
-    const seeOthers = Link.create(
-        'chrome://serviceworker-internals', i18nString(UIStrings.seeAllRegistrations), undefined, 'view-all');
+    const seeOthers = Link.create('chrome://serviceworker-internals', i18nString(UIStrings.seeAllRegistrations),
+                                  undefined, 'view-all', 0, /* allowPrivileged=*/ true);
     othersSectionRow.appendChild(seeOthers);
 
     this.toolbar.appendToolbarItem(
diff --git a/front_end/panels/application/components/BackForwardCacheView.ts b/front_end/panels/application/components/BackForwardCacheView.ts
index 5415b48..76cd6e7 100644
--- a/front_end/panels/application/components/BackForwardCacheView.ts
+++ b/front_end/panels/application/components/BackForwardCacheView.ts
@@ -341,7 +341,7 @@
     const link = 'chrome://extensions/?id=' + explanation.context as Platform.DevToolsPath.UrlString;
     // clang-format off
     return html`${i18nString(UIStrings.blockingExtensionId)}
-      <devtools-link .href=${link}>${explanation.context}</devtools-link>`;
+      <devtools-link .href=${link} allow-privileged>${explanation.context}</devtools-link>`;
     // clang-format on
   }
   return nothing;
diff --git a/front_end/panels/settings/components/SyncSection.ts b/front_end/panels/settings/components/SyncSection.ts
index 6c82270..f4cd8bf 100644
--- a/front_end/panels/settings/components/SyncSection.ts
+++ b/front_end/panels/settings/components/SyncSection.ts
@@ -346,7 +346,7 @@
     // TODO: investigate if /advance link is alive
     const warningLink =
         this.#syncInfo.isSyncActive ? 'chrome://settings/syncSetup/advanced' : 'chrome://settings/syncSetup';
-    UIHelpers.openInNewTab(warningLink);
+    UIHelpers.openInNewTab(warningLink, /* allowPrivileged=*/ true);
     event.consume();
   }
 
diff --git a/front_end/ui/helpers/OpenInNewTab.test.ts b/front_end/ui/helpers/OpenInNewTab.test.ts
index b6b3232..3c564ed 100644
--- a/front_end/ui/helpers/OpenInNewTab.test.ts
+++ b/front_end/ui/helpers/OpenInNewTab.test.ts
@@ -147,14 +147,27 @@
   });
 
   describeWithMockConnection('chrome:// link', () => {
-    it('call the correct API for chrome:// links', async () => {
+    it('calls invoke_createTarget for chrome:// links when privileged navigation is allowed', async () => {
       const target = createTarget();
       const spy = sinon.spy(target.targetAgent(), 'invoke_createTarget');
 
-      openInNewTab('chrome://settings');
+      openInNewTab('chrome://settings', /* allowPrivileged=*/ true);
 
       sinon.assert.calledOnce(spy);
       assert.deepEqual(spy.firstCall.firstArg, {url: 'chrome://settings/'});
     });
+
+    it('falls back to InspectorFrontendHost for chrome:// links when privileged navigation is not allowed',
+       async () => {
+         const target = createTarget();
+         const spy = sinon.spy(target.targetAgent(), 'invoke_createTarget');
+         const stub = sinon.stub(InspectorFrontendHostInstance, 'openInNewTab');
+
+         openInNewTab('chrome://settings');
+
+         sinon.assert.notCalled(spy);
+         sinon.assert.calledOnce(stub);
+         assert.strictEqual(stub.args[0][0], 'chrome://settings/');
+       });
   });
 });
diff --git a/front_end/ui/helpers/OpenInNewTab.ts b/front_end/ui/helpers/OpenInNewTab.ts
index cfa93f3..9032efc 100644
--- a/front_end/ui/helpers/OpenInNewTab.ts
+++ b/front_end/ui/helpers/OpenInNewTab.ts
@@ -57,15 +57,15 @@
  * @throws TypeError if `url` is not a valid URL.
  * @see https://en.wikipedia.org/wiki/UTM_parameters
  */
-export function openInNewTab(url: URL|string): void {
+export function openInNewTab(url: URL|string, allowPrivileged?: boolean): void {
   url = new URL(url);
   if (Common.ParsedURL.schemeIs(url, 'javascript:')) {
     return;
   }
 
   // Navigating to a chrome:// link via a normal anchor doesn't work, so we "navigate"
-  // there using CDP.
-  if (Common.ParsedURL.schemeIs(url, 'chrome:')) {
+  // there using CDP if explicitly requested.
+  if (allowPrivileged && Common.ParsedURL.schemeIs(url, 'chrome:')) {
     const rootTarget = SDK.TargetManager.TargetManager.instance().rootTarget();
     if (rootTarget === null) {
       return;
diff --git a/front_end/ui/kit/link/Link.test.ts b/front_end/ui/kit/link/Link.test.ts
index 9eaef54..c8af7d5 100644
--- a/front_end/ui/kit/link/Link.test.ts
+++ b/front_end/ui/kit/link/Link.test.ts
@@ -42,6 +42,27 @@
     });
   });
 
+  describe('allowPrivileged', () => {
+    it('sets allow-privileged attribute', () => {
+      const link = new Link();
+      link.allowPrivileged = true;
+      assert.isTrue(link.hasAttribute('allow-privileged'));
+    });
+
+    it('unsets allow-privileged attribute', () => {
+      const link = new Link();
+      link.allowPrivileged = true;
+      link.allowPrivileged = false;
+      assert.isFalse(link.hasAttribute('allow-privileged'));
+    });
+
+    it('reads allow-privileged attribute', () => {
+      const link = new Link();
+      link.setAttribute('allow-privileged', '');
+      assert.isTrue(link.allowPrivileged);
+    });
+  });
+
   describe('visual logging', () => {
     it('should default to empty link', () => {
       const link = new Link();
diff --git a/front_end/ui/kit/link/Link.ts b/front_end/ui/kit/link/Link.ts
index b60cb58..d0d5e6a 100644
--- a/front_end/ui/kit/link/Link.ts
+++ b/front_end/ui/kit/link/Link.ts
@@ -60,7 +60,7 @@
       return;
     }
 
-    UIHelpers.openInNewTab(href);
+    UIHelpers.openInNewTab(href, this.allowPrivileged);
 
     event.consume();
   }
@@ -73,6 +73,18 @@
     this.setAttribute('href', href);
   }
 
+  get allowPrivileged(): boolean {
+    return this.hasAttribute('allow-privileged');
+  }
+
+  set allowPrivileged(allowPrivileged: boolean) {
+    if (allowPrivileged) {
+      this.setAttribute('allow-privileged', '');
+    } else {
+      this.removeAttribute('allow-privileged');
+    }
+  }
+
   get jslogContext(): string|null {
     return this.getAttribute('jslogcontext');
   }
@@ -98,7 +110,7 @@
       oldValue: string|null,
       newValue: string|null,
       ): void {
-    if (oldValue !== newValue) {
+    if (oldValue === newValue) {
       return;
     }
     if (name === 'jslogcontext') {
@@ -145,9 +157,11 @@
       className?: string,
       jsLogContext?: string,
       tabindex = 0,
+      allowPrivileged = false,
       ): Link {
     const link = new Link();
     link.href = url as Platform.DevToolsPath.UrlString;
+    link.allowPrivileged = allowPrivileged;
     linkText = linkText ?? url;
     link.textContent = Platform.StringUtilities.trimMiddle(linkText, 150);
 
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.