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:
commit
e2d6196a39
1 changed files with 32 additions and 11 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue