Merge pull request #230 from modelcontextprotocol/fix/chrome-applescript-tab-id-sanitize

fix(examples/chrome-applescript): sanitize tab_id to prevent AppleScript injection
This commit is contained in:
Tobin South 2026-04-17 12:12:41 -07:00 • committed by GitHub
commit e2d6196a39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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) { async findTabById(tabId) {
const safeTabId = this.toSafeTabId(tabId);
if (safeTabId === null) {
return { w: null, t: null, found: false };
}
const script = ` const script = `
tell application "Google Chrome" tell application "Google Chrome"
repeat with w in windows repeat with w in windows
repeat with t in tabs of w 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} return {w, t, true}
end if end if
end repeat end repeat
@ -103,7 +119,8 @@ class ChromeControlServer {
operation, operation,
successMessage = "Operation completed", successMessage = "Operation completed",
) { ) {
if (!tabId) { const safeTabId = this.toSafeTabId(tabId);
if (safeTabId === null) {
// No tab ID, execute on active tab // No tab ID, execute on active tab
return this.executeAppleScript(operation.activeScript); return this.executeAppleScript(operation.activeScript);
} }
@ -112,7 +129,7 @@ class ChromeControlServer {
tell application "Google Chrome" tell application "Google Chrome"
repeat with w in windows repeat with w in windows
repeat with t in tabs of w 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} ${operation.tabScript}
return "${successMessage}" return "${successMessage}"
end if end if
@ -551,6 +568,7 @@ class ChromeControlServer {
"JavaScript code is required and must be a string", "JavaScript code is required and must be a string",
); );
} }
const safeTabId = this.toSafeTabId(tab_id);
// Wrap the code to capture console.log outputs // Wrap the code to capture console.log outputs
const wrappedCode = ` const wrappedCode = `
@ -628,12 +646,13 @@ class ChromeControlServer {
const escapedCode = this.escapeForAppleScript(wrappedCode); const escapedCode = this.escapeForAppleScript(wrappedCode);
const script = tab_id const script =
? ` safeTabId != null
? `
tell application "Google Chrome" tell application "Google Chrome"
repeat with w in windows repeat with w in windows
repeat with t in tabs of w 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}" set result to execute t javascript "${escapedCode}"
return result return result
end if end if
@ -642,7 +661,7 @@ class ChromeControlServer {
return "Tab not found" return "Tab not found"
end tell end tell
` `
: ` : `
tell application "Google Chrome" tell application "Google Chrome"
execute active tab of front window javascript "${escapedCode}" execute active tab of front window javascript "${escapedCode}"
end tell end tell
@ -695,14 +714,16 @@ class ChromeControlServer {
case "get_page_content": { case "get_page_content": {
const { tab_id } = args; const { tab_id } = args;
const safeTabId = this.toSafeTabId(tab_id);
const jsCode = "document.body.innerText"; const jsCode = "document.body.innerText";
const script = tab_id const script =
? ` safeTabId != null
? `
tell application "Google Chrome" tell application "Google Chrome"
repeat with w in windows repeat with w in windows
repeat with t in tabs of w 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}" set pageContent to execute t javascript "${jsCode}"
return pageContent return pageContent
end if end if
@ -711,7 +732,7 @@ class ChromeControlServer {
return "Tab not found" return "Tab not found"
end tell end tell
` `
: ` : `
tell application "Google Chrome" tell application "Google Chrome"
execute active tab of front window javascript "${jsCode}" execute active tab of front window javascript "${jsCode}"
end tell end tell