Skip to content

Commit e2d6196

Browse files
authored
Merge pull request #230 from modelcontextprotocol/fix/chrome-applescript-tab-id-sanitize
fix(examples/chrome-applescript): sanitize tab_id to prevent AppleScript injection
2 parents a3a6624 + dace08d commit e2d6196

1 file changed

Lines changed: 32 additions & 11 deletions

File tree

  • examples/chrome-applescript/server

examples/chrome-applescript/server/index.js

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,28 @@ class ChromeControlServer {
8282
}
8383
}
8484

85+
// Coerce tab_id to a safe numeric value before interpolating into
86+
// AppleScript. inputSchema type:"number" is advisory — runtime may receive
87+
// a string, which would otherwise be injected into the osascript template.
88+
toSafeTabId(tabId) {
89+
if (tabId === null || tabId === undefined) return null;
90+
const n = Number.parseInt(tabId, 10);
91+
if (Number.isNaN(n)) {
92+
throw new Error("tab_id must be a number");
93+
}
94+
return n;
95+
}
96+
8597
async findTabById(tabId) {
98+
const safeTabId = this.toSafeTabId(tabId);
99+
if (safeTabId === null) {
100+
return { w: null, t: null, found: false };
101+
}
86102
const script = `
87103
tell application "Google Chrome"
88104
repeat with w in windows
89105
repeat with t in tabs of w
90-
if (id of t as string) is "${tabId}" then
106+
if (id of t as string) is "${safeTabId}" then
91107
return {w, t, true}
92108
end if
93109
end repeat
@@ -103,7 +119,8 @@ class ChromeControlServer {
103119
operation,
104120
successMessage = "Operation completed",
105121
) {
106-
if (!tabId) {
122+
const safeTabId = this.toSafeTabId(tabId);
123+
if (safeTabId === null) {
107124
// No tab ID, execute on active tab
108125
return this.executeAppleScript(operation.activeScript);
109126
}
@@ -112,7 +129,7 @@ class ChromeControlServer {
112129
tell application "Google Chrome"
113130
repeat with w in windows
114131
repeat with t in tabs of w
115-
if (id of t as string) is "${tabId}" then
132+
if (id of t as string) is "${safeTabId}" then
116133
${operation.tabScript}
117134
return "${successMessage}"
118135
end if
@@ -551,6 +568,7 @@ class ChromeControlServer {
551568
"JavaScript code is required and must be a string",
552569
);
553570
}
571+
const safeTabId = this.toSafeTabId(tab_id);
554572

555573
// Wrap the code to capture console.log outputs
556574
const wrappedCode = `
@@ -628,12 +646,13 @@ class ChromeControlServer {
628646

629647
const escapedCode = this.escapeForAppleScript(wrappedCode);
630648

631-
const script = tab_id
632-
? `
649+
const script =
650+
safeTabId != null
651+
? `
633652
tell application "Google Chrome"
634653
repeat with w in windows
635654
repeat with t in tabs of w
636-
if (id of t as string) is "${tab_id}" then
655+
if (id of t as string) is "${safeTabId}" then
637656
set result to execute t javascript "${escapedCode}"
638657
return result
639658
end if
@@ -642,7 +661,7 @@ class ChromeControlServer {
642661
return "Tab not found"
643662
end tell
644663
`
645-
: `
664+
: `
646665
tell application "Google Chrome"
647666
execute active tab of front window javascript "${escapedCode}"
648667
end tell
@@ -695,14 +714,16 @@ class ChromeControlServer {
695714

696715
case "get_page_content": {
697716
const { tab_id } = args;
717+
const safeTabId = this.toSafeTabId(tab_id);
698718

699719
const jsCode = "document.body.innerText";
700-
const script = tab_id
701-
? `
720+
const script =
721+
safeTabId != null
722+
? `
702723
tell application "Google Chrome"
703724
repeat with w in windows
704725
repeat with t in tabs of w
705-
if (id of t as string) is "${tab_id}" then
726+
if (id of t as string) is "${safeTabId}" then
706727
set pageContent to execute t javascript "${jsCode}"
707728
return pageContent
708729
end if
@@ -711,7 +732,7 @@ class ChromeControlServer {
711732
return "Tab not found"
712733
end tell
713734
`
714-
: `
735+
: `
715736
tell application "Google Chrome"
716737
execute active tab of front window javascript "${jsCode}"
717738
end tell

0 commit comments

Comments
 (0)