From 3b846b3d79fd0ab108328fcac2bfdca62a3f556b Mon Sep 17 00:00:00 2001 From: jackwener Date: Sun, 22 Mar 2026 22:03:59 +0800 Subject: [PATCH] fix: replace all about:blank with data: URI to prevent New Tab Override interception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: getAutomationWindow and resolveTabId used about:blank which New Tab Override extensions intercept immediately, replacing it with chrome-extension:// URLs that cannot be debugged. Changes: - Window creation: about:blank → data:text/html - reuseTab fallback: about:blank → data:text/html - newTab handler: about:blank → data:text/html - Added diagnostic logging to resolveTabId for debugging - Synced extension version to 1.2.4 Ref: #249 --- extension/dist/background.js | 15 ++++++++++----- extension/manifest.json | 2 +- extension/package.json | 2 +- extension/src/background.ts | 18 ++++++++++++------ package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/extension/dist/background.js b/extension/dist/background.js index d75f1e45..a4570021 100644 --- a/extension/dist/background.js +++ b/extension/dist/background.js @@ -228,7 +228,7 @@ async function getAutomationWindow(workspace) { } } const win = await chrome.windows.create({ - url: "about:blank", + url: "data:text/html,", focused: false, width: 1280, height: 900, @@ -309,6 +309,7 @@ async function resolveTabId(tabId, workspace) { if (tabId !== void 0) { try { const tab = await chrome.tabs.get(tabId); + console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`); if (isDebuggableUrl(tab.url)) return tabId; console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`); } catch { @@ -318,10 +319,14 @@ async function resolveTabId(tabId, workspace) { const windowId = await getAutomationWindow(workspace); const tabs = await chrome.tabs.query({ windowId }); const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url)); - if (debuggableTab?.id) return debuggableTab.id; + if (debuggableTab?.id) { + console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`); + return debuggableTab.id; + } + console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map((t) => `${t.id}=${t.url}`).join(", ")}`); const reuseTab = tabs.find((t) => t.id); if (reuseTab?.id) { - await chrome.tabs.update(reuseTab.id, { url: "about:blank" }); + await chrome.tabs.update(reuseTab.id, { url: "data:text/html," }); await new Promise((resolve) => setTimeout(resolve, 300)); try { const updated = await chrome.tabs.get(reuseTab.id); @@ -335,7 +340,7 @@ async function resolveTabId(tabId, workspace) { } catch { } } - const newTab = await chrome.tabs.create({ windowId, url: "about:blank", active: true }); + const newTab = await chrome.tabs.create({ windowId, url: "data:text/html,", active: true }); if (!newTab.id) throw new Error("Failed to create tab in automation window"); return newTab.id; } @@ -423,7 +428,7 @@ async function handleTabs(cmd, workspace) { } case "new": { const windowId = await getAutomationWindow(workspace); - const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "about:blank", active: true }); + const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "data:text/html,", active: true }); return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } }; } case "close": { diff --git a/extension/manifest.json b/extension/manifest.json index 3a147e17..b438cd81 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "OpenCLI", - "version": "0.2.0", + "version": "1.2.4", "description": "Bridge between opencli CLI and your browser — execute commands, read cookies, manage tabs.", "permissions": [ "debugger", diff --git a/extension/package.json b/extension/package.json index c39adb7a..617a8e03 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "opencli-extension", - "version": "0.2.0", + "version": "1.2.4", "private": true, "type": "module", "scripts": { diff --git a/extension/src/background.ts b/extension/src/background.ts index 75aa8586..53278314 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -135,9 +135,10 @@ async function getAutomationWindow(workspace: string): Promise { } } - // Create a new window with about:blank (not chrome://newtab which blocks scripting) + // Create a new window with a data: URI that New Tab Override extensions cannot intercept. + // Using about:blank would be hijacked by extensions like "New Tab Override". const win = await chrome.windows.create({ - url: 'about:blank', + url: 'data:text/html,', focused: false, width: 1280, height: 900, @@ -244,6 +245,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi if (tabId !== undefined) { try { const tab = await chrome.tabs.get(tabId); + console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`); if (isDebuggableUrl(tab.url)) return tabId; // Tab exists but URL is not debuggable — fall through to auto-resolve console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`); @@ -259,7 +261,11 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi // Prefer an existing debuggable tab (about:blank, http://, https://, etc.) const tabs = await chrome.tabs.query({ windowId }); const debuggableTab = tabs.find(t => t.id && isDebuggableUrl(t.url)); - if (debuggableTab?.id) return debuggableTab.id; + if (debuggableTab?.id) { + console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`); + return debuggableTab.id; + } + console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map(t => `${t.id}=${t.url}`).join(', ')}`); // No debuggable tab found — this typically happens when a "New Tab Override" // extension replaces about:blank with a chrome-extension:// page. @@ -267,7 +273,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi // accumulating orphan tabs if chrome.tabs.create is also intercepted). const reuseTab = tabs.find(t => t.id); if (reuseTab?.id) { - await chrome.tabs.update(reuseTab.id, { url: 'about:blank' }); + await chrome.tabs.update(reuseTab.id, { url: 'data:text/html,' }); // Wait for the navigation to take effect await new Promise(resolve => setTimeout(resolve, 300)); // Verify the URL is actually debuggable (New Tab Override may have intercepted) @@ -288,7 +294,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi } // Window has no debuggable tabs — create one - const newTab = await chrome.tabs.create({ windowId, url: 'about:blank', active: true }); + const newTab = await chrome.tabs.create({ windowId, url: 'data:text/html,', active: true }); if (!newTab.id) throw new Error('Failed to create tab in automation window'); return newTab.id; } @@ -397,7 +403,7 @@ async function handleTabs(cmd: Command, workspace: string): Promise { } case 'new': { const windowId = await getAutomationWindow(workspace); - const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'about:blank', active: true }); + const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'data:text/html,', active: true }); return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } }; } case 'close': { diff --git a/package-lock.json b/package-lock.json index 0a1c979a..3f509a51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jackwener/opencli", - "version": "1.2.3", + "version": "1.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jackwener/opencli", - "version": "1.2.3", + "version": "1.2.4", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index d40f292d..9172122f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jackwener/opencli", - "version": "1.2.3", + "version": "1.2.4", "publishConfig": { "access": "public" },