From dace08d31fa89e7026bcee17d4e85407a580e311 Mon Sep 17 00:00:00 2001 From: Bryan Thompson Date: Fri, 17 Apr 2026 13:49:30 -0500 Subject: [PATCH] fix(examples/chrome-applescript): sanitize tab_id to prevent AppleScript injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inputSchema `type: "number"` is advisory only — runtime may receive a string payload that gets interpolated into the osascript template. Coerce tab_id via Number.parseInt before interpolating at all four sites. Affected sites: - findTabById(tabId) - executeTabOperation(tabId, ...) (close_tab, switch_to_tab, reload_tab, go_back, go_forward) - execute_javascript handler - get_page_content handler The shared toSafeTabId() helper returns null for missing values (preserving the active-tab fallthrough) and throws on non-numeric input (surfacing misuse via the existing request-level try/catch, consistent with the missing-code path in execute_javascript). --- examples/chrome-applescript/server/index.js | 43 +++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/examples/chrome-applescript/server/index.js b/examples/chrome-applescript/server/index.js index dd96bdd..8ffb778 100644 --- a/examples/chrome-applescript/server/index.js +++ b/examples/chrome-applescript/server/index.js @@ -82,12 +82,28 @@ class ChromeControlServer { } } + // Coerce tab_id to a safe numeric value before interpolating into + // AppleScript. inputSchema type:"number" is advisory — runtime may receive + // a string, which would otherwise be injected into the osascript template. + toSafeTabId(tabId) { + if (tabId === null || tabId === undefined) return null; + const n = Number.parseInt(tabId, 10); + if (Number.isNaN(n)) { + throw new Error("tab_id must be a number"); + } + return n; + } + async findTabById(tabId) { + const safeTabId = this.toSafeTabId(tabId); + if (safeTabId === null) { + return { w: null, t: null, found: false }; + } const script = ` tell application "Google Chrome" repeat with w in windows repeat with t in tabs of w - if (id of t as string) is "${tabId}" then + if (id of t as string) is "${safeTabId}" then return {w, t, true} end if end repeat @@ -103,7 +119,8 @@ class ChromeControlServer { operation, successMessage = "Operation completed", ) { - if (!tabId) { + const safeTabId = this.toSafeTabId(tabId); + if (safeTabId === null) { // No tab ID, execute on active tab return this.executeAppleScript(operation.activeScript); } @@ -112,7 +129,7 @@ class ChromeControlServer { tell application "Google Chrome" repeat with w in windows repeat with t in tabs of w - if (id of t as string) is "${tabId}" then + if (id of t as string) is "${safeTabId}" then ${operation.tabScript} return "${successMessage}" end if @@ -551,6 +568,7 @@ class ChromeControlServer { "JavaScript code is required and must be a string", ); } + const safeTabId = this.toSafeTabId(tab_id); // Wrap the code to capture console.log outputs const wrappedCode = ` @@ -628,12 +646,13 @@ class ChromeControlServer { const escapedCode = this.escapeForAppleScript(wrappedCode); - const script = tab_id - ? ` + const script = + safeTabId != null + ? ` tell application "Google Chrome" repeat with w in windows repeat with t in tabs of w - if (id of t as string) is "${tab_id}" then + if (id of t as string) is "${safeTabId}" then set result to execute t javascript "${escapedCode}" return result end if @@ -642,7 +661,7 @@ class ChromeControlServer { return "Tab not found" end tell ` - : ` + : ` tell application "Google Chrome" execute active tab of front window javascript "${escapedCode}" end tell @@ -695,14 +714,16 @@ class ChromeControlServer { case "get_page_content": { const { tab_id } = args; + const safeTabId = this.toSafeTabId(tab_id); const jsCode = "document.body.innerText"; - const script = tab_id - ? ` + const script = + safeTabId != null + ? ` tell application "Google Chrome" repeat with w in windows repeat with t in tabs of w - if (id of t as string) is "${tab_id}" then + if (id of t as string) is "${safeTabId}" then set pageContent to execute t javascript "${jsCode}" return pageContent end if @@ -711,7 +732,7 @@ class ChromeControlServer { return "Tab not found" end tell ` - : ` + : ` tell application "Google Chrome" execute active tab of front window javascript "${jsCode}" end tell