Chrome · DevTools
CVE-2026-14117
Logic Error in DevTools
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
front_end/panels/sources/PersistenceAction.test.tsfront_end/panels/sources/PersistenceActions.ts
Patch
From 6c2d684f3933381f002c34d44e05f4b19b4da3a1 Mon Sep 17 00:00:00 2001
From: Wolfgang Beyer <wolfi@chromium.org>
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 <wolfi@chromium.org>
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Wolfgang Beyer <wolfi@chromium.org>
---
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.
References
On This Page