Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in DevTools
DescriptionInsufficient validation of untrusted input in DevTools
ComponentDevTools
Bug ClassLogic Error
Tracker513751020
Fix commit6c2d684f3933 (devtools/devtools-frontend) +113/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • front_end/panels/sources/PersistenceAction.test.ts
  • front_end/panels/sources/PersistenceActions.ts
From 6c2d684f3933381f002c34d44e05f4b19b4da3a1 Mon Sep 17 00:00:00 2001
From: Wolfgang Beyer <[email protected]>
Date: Wed, 27 May 2026 10:39:53 +0000
Subject: [PATCH] Limit 'open in containing folder' to FileSystem

Bug: 513751020
Change-Id: I69496f779cea9a00f29a3c7821b838c789d37124
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7867926
Auto-Submit: Wolfgang Beyer <[email protected]>
Reviewed-by: Alex Rudenko <[email protected]>
Commit-Queue: Alex Rudenko <[email protected]>
Commit-Queue: Wolfgang Beyer <[email protected]>
---

diff --git a/front_end/panels/sources/PersistenceAction.test.ts b/front_end/panels/sources/PersistenceAction.test.ts
index 974bcd2..c786ec5 100644
--- a/front_end/panels/sources/PersistenceAction.test.ts
+++ b/front_end/panels/sources/PersistenceAction.test.ts
@@ -94,4 +94,96 @@
       /* forceSaveAs=*/ true,
     ]);
   });
+
+  describe('Open in containing folder', () => {
+    function createMockUISourceCode(
+        url: Platform.DevToolsPath.UrlString, projectType: Workspace.Workspace.projectTypes):
+        sinon.SinonStubbedInstance<Workspace.UISourceCode.UISourceCode> {
+      const uiSourceCode = sinon.createStubInstance(Workspace.UISourceCode.UISourceCode, {
+        contentURL: url,
+        contentType: Common.ResourceType.resourceTypes.Script,
+      });
+      const stubProject = sinon.createStubInstance(
+          Bindings.ContentProviderBasedProject.ContentProviderBasedProject, {type: projectType});
+      uiSourceCode.project.returns(stubProject);
+      return uiSourceCode;
+    }
+
+    function setupSingletons(
+        uiSourceCode: Workspace.UISourceCode.UISourceCode|null,
+        binding: Persistence.Persistence.PersistenceBinding|null): void {
+      const mockWorkspaceImpl = sinon.createStubInstance(Workspace.Workspace.WorkspaceImpl);
+      sinon.stub(Workspace.Workspace.WorkspaceImpl, 'instance').returns(mockWorkspaceImpl);
+      mockWorkspaceImpl.uiSourceCodeForURL.returns(uiSourceCode);
+
+      const mockPersistenceImpl = sinon.createStubInstance(Persistence.Persistence.PersistenceImpl);
+      sinon.stub(Persistence.Persistence.PersistenceImpl, 'instance').returns(mockPersistenceImpl);
+      mockPersistenceImpl.binding.returns(binding);
+    }
+
+    async function getOpenInFolderMenuItem(targetUiSourceCode: Workspace.UISourceCode.UISourceCode):
+        Promise<UI.ContextMenu.Item|undefined> {
+      const event = new Event('contextmenu');
+      sinon.stub(event, 'target').value(document);
+      const contextMenu = new UI.ContextMenu.ContextMenu(event);
+      const menuProvider = new Sources.PersistenceActions.ContextMenuProvider();
+
+      menuProvider.appendApplicableItems(event, contextMenu, targetUiSourceCode);
+      await contextMenu.show();
+
+      return contextMenu.revealSection().items.find(
+          item => item.buildDescriptor().label === 'Open in containing folder');
+    }
+
+    it('appends the item for direct workspace files (FileSystem project type)', async () => {
+      const uiSourceCode = createMockUISourceCode(
+          urlString`file:///path/to/project/file.js`, Workspace.Workspace.projectTypes.FileSystem);
+      setupSingletons(uiSourceCode, null);
+
+      const openFolderItem = await getOpenInFolderMenuItem(uiSourceCode);
+      assert.exists(openFolderItem);
+    });
+
+    it('appends the item for network files mapped to workspace files (persistence binding exists)', async () => {
+      const networkUiSourceCode =
+          createMockUISourceCode(urlString`https://example.com/file.js`, Workspace.Workspace.projectTypes.Network);
+      const fileSystemUiSourceCode = createMockUISourceCode(
+          urlString`file:///path/to/project/file.js`, Workspace.Workspace.projectTypes.FileSystem);
+
+      const binding = new Persistence.Persistence.PersistenceBinding(networkUiSourceCode, fileSystemUiSourceCode);
+      setupSingletons(networkUiSourceCode, binding);
+
+      const openFolderItem = await getOpenInFolderMenuItem(networkUiSourceCode);
+      assert.exists(openFolderItem);
+    });
+
+    it('does not append the item for local file page resources that are not in a FileSystem project type', async () => {
+      const uiSourceCode =
+          createMockUISourceCode(urlString`file:///path/to/project/file.js`, Workspace.Workspace.projectTypes.Network);
+      setupSingletons(uiSourceCode, null);
+
+      const openFolderItem = await getOpenInFolderMenuItem(uiSourceCode);
+      assert.isUndefined(openFolderItem);
+    });
+
+    it('appends the item if contentProvider is a FileSystem UISourceCode but uiSourceCodeForURL returns a Network UISourceCode (same URL, not bound)',
+       async () => {
+         const fileSystemUiSourceCode = createMockUISourceCode(
+             urlString`file:///path/to/project/file.js`, Workspace.Workspace.projectTypes.FileSystem);
+         const networkUiSourceCode = createMockUISourceCode(
+             urlString`file:///path/to/project/file.js`, Workspace.Workspace.projectTypes.Network);
+
+         // uiSourceCodeForURL returns the Network UISourceCode due to order.
+         const mockWorkspaceImpl = sinon.createStubInstance(Workspace.Workspace.WorkspaceImpl);
+         sinon.stub(Workspace.Workspace.WorkspaceImpl, 'instance').returns(mockWorkspaceImpl);
+         mockWorkspaceImpl.uiSourceCodeForURL.returns(networkUiSourceCode);
+
+         const mockPersistenceImpl = sinon.createStubInstance(Persistence.Persistence.PersistenceImpl);
+         sinon.stub(Persistence.Persistence.PersistenceImpl, 'instance').returns(mockPersistenceImpl);
+         mockPersistenceImpl.binding.returns(null);
+
+         const openFolderItem = await getOpenInFolderMenuItem(fileSystemUiSourceCode);
+         assert.exists(openFolderItem);
+       });
+  });
 });
diff --git a/front_end/panels/sources/PersistenceActions.ts b/front_end/panels/sources/PersistenceActions.ts
index ac0923d..316fa5a 100644
--- a/front_end/panels/sources/PersistenceActions.ts
+++ b/front_end/panels/sources/PersistenceActions.ts
@@ -115,14 +115,28 @@
     const networkPersistenceManager = Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance();
 
     const binding = uiSourceCode && Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
-    const fileURL = binding ? binding.fileSystem.contentURL() : contentProvider.contentURL();
+    // If contentProvider is already a FileSystem UISourceCode, there's a small chance that uiSourceCodeForURL
+    // might return a Network UISourceCode instead (e.g. if they share the same URL but aren't currently bound
+    // and the Network project represents the URL first in the lookup list).
+    // In this case, the binding will be null. To ensure "Open in containing folder" is still appended when
+    // right-clicking the FileSystem file in the Workspace tree, we fall back to contentProvider.
+    const fileSystemUISourceCode = binding ?
+        binding.fileSystem :
+        (contentProvider instanceof Workspace.UISourceCode.UISourceCode &&
+                 contentProvider.project().type() === Workspace.Workspace.projectTypes.FileSystem ?
+             contentProvider :
+             uiSourceCode);
 
-    if (Common.ParsedURL.schemeIs(fileURL, 'file:')) {
-      const path = Common.ParsedURL.ParsedURL.urlToRawPathString(fileURL, Host.Platform.isWin());
-      contextMenu.revealSection().appendItem(
-          i18nString(UIStrings.openInContainingFolder),
-          () => Host.InspectorFrontendHost.InspectorFrontendHostInstance.showItemInFolder(path),
-          {jslogContext: 'open-in-containing-folder'});
+    if (fileSystemUISourceCode &&
+        fileSystemUISourceCode.project().type() === Workspace.Workspace.projectTypes.FileSystem) {
+      const fileURL = fileSystemUISourceCode.contentURL();
+      if (Common.ParsedURL.schemeIs(fileURL, 'file:')) {
+        const path = Common.ParsedURL.ParsedURL.urlToRawPathString(fileURL, Host.Platform.isWin());
+        contextMenu.revealSection().appendItem(
+            i18nString(UIStrings.openInContainingFolder),
+            () => Host.InspectorFrontendHost.InspectorFrontendHostInstance.showItemInFolder(path),
+            {jslogContext: 'open-in-containing-folder'});
+      }
     }
 
     if (contentProvider instanceof Workspace.UISourceCode.UISourceCode &&
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.