mirror of
https://github.com/AgentDeskAI/browser-tools-mcp.git
synced 2025-11-17 18:51:48 +00:00
Add server identity verification to prevent connecting to incorrect web servers
This commit is contained in:
parent
a71e00d393
commit
60248f9fca
@ -418,6 +418,16 @@ app.get("/.port", (req, res) => {
|
|||||||
res.send(PORT.toString());
|
res.send(PORT.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add new identity endpoint with a unique signature
|
||||||
|
app.get("/.identity", (req, res) => {
|
||||||
|
res.json({
|
||||||
|
port: PORT,
|
||||||
|
name: "browser-tools-server",
|
||||||
|
version: "1.1.0",
|
||||||
|
signature: "mcp-browser-connector-24x7",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add function to clear all logs
|
// Add function to clear all logs
|
||||||
function clearAllLogs() {
|
function clearAllLogs() {
|
||||||
console.log("Wiping all logs...");
|
console.log("Wiping all logs...");
|
||||||
|
|||||||
@ -8,94 +8,150 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||||||
serverPort: 3025,
|
serverPort: 3025,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the inspected window's tab
|
// Validate server identity first
|
||||||
chrome.tabs.get(message.tabId, (tab) => {
|
validateServerIdentity(settings.serverHost, settings.serverPort)
|
||||||
if (chrome.runtime.lastError) {
|
.then((isValid) => {
|
||||||
console.error("Error getting tab:", chrome.runtime.lastError);
|
if (!isValid) {
|
||||||
sendResponse({
|
console.error(
|
||||||
success: false,
|
"Cannot capture screenshot: Not connected to a valid browser tools server"
|
||||||
error: chrome.runtime.lastError.message,
|
);
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all windows to find the one containing our tab
|
|
||||||
chrome.windows.getAll({ populate: true }, (windows) => {
|
|
||||||
const targetWindow = windows.find((w) =>
|
|
||||||
w.tabs.some((t) => t.id === message.tabId)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!targetWindow) {
|
|
||||||
console.error("Could not find window containing the inspected tab");
|
|
||||||
sendResponse({
|
sendResponse({
|
||||||
success: false,
|
success: false,
|
||||||
error: "Could not find window containing the inspected tab",
|
error:
|
||||||
|
"Not connected to a valid browser tools server. Please check your connection settings.",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture screenshot of the window containing our tab
|
// Continue with screenshot capture
|
||||||
chrome.tabs.captureVisibleTab(
|
captureAndSendScreenshot(message, settings, sendResponse);
|
||||||
targetWindow.id,
|
})
|
||||||
{ format: "png" },
|
.catch((error) => {
|
||||||
(dataUrl) => {
|
console.error("Error validating server:", error);
|
||||||
// Ignore DevTools panel capture error if it occurs
|
sendResponse({
|
||||||
if (
|
success: false,
|
||||||
chrome.runtime.lastError &&
|
error: "Failed to validate server identity: " + error.message,
|
||||||
!chrome.runtime.lastError.message.includes("devtools://")
|
});
|
||||||
) {
|
|
||||||
console.error(
|
|
||||||
"Error capturing screenshot:",
|
|
||||||
chrome.runtime.lastError
|
|
||||||
);
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
error: chrome.runtime.lastError.message,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send screenshot data to browser connector using configured settings
|
|
||||||
const serverUrl = `http://${settings.serverHost}:${settings.serverPort}/screenshot`;
|
|
||||||
console.log(`Sending screenshot to ${serverUrl}`);
|
|
||||||
|
|
||||||
fetch(serverUrl, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
data: dataUrl,
|
|
||||||
path: message.screenshotPath,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((result) => {
|
|
||||||
if (result.error) {
|
|
||||||
console.error("Error from server:", result.error);
|
|
||||||
sendResponse({ success: false, error: result.error });
|
|
||||||
} else {
|
|
||||||
console.log("Screenshot saved successfully:", result.path);
|
|
||||||
// Send success response even if DevTools capture failed
|
|
||||||
sendResponse({
|
|
||||||
success: true,
|
|
||||||
path: result.path,
|
|
||||||
title: tab.title || "Current Tab",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error sending screenshot data:", error);
|
|
||||||
sendResponse({
|
|
||||||
success: false,
|
|
||||||
error: error.message || "Failed to save screenshot",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return true; // Required to use sendResponse asynchronously
|
return true; // Required to use sendResponse asynchronously
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Validate server identity
|
||||||
|
async function validateServerIdentity(host, port) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${host}:${port}/.identity`, {
|
||||||
|
signal: AbortSignal.timeout(3000), // 3 second timeout
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Invalid server response: ${response.status}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = await response.json();
|
||||||
|
|
||||||
|
// Validate the server signature
|
||||||
|
if (identity.signature !== "mcp-browser-connector-24x7") {
|
||||||
|
console.error("Invalid server signature - not the browser tools server");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error validating server identity:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to capture and send screenshot
|
||||||
|
function captureAndSendScreenshot(message, settings, sendResponse) {
|
||||||
|
// Get the inspected window's tab
|
||||||
|
chrome.tabs.get(message.tabId, (tab) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Error getting tab:", chrome.runtime.lastError);
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
error: chrome.runtime.lastError.message,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all windows to find the one containing our tab
|
||||||
|
chrome.windows.getAll({ populate: true }, (windows) => {
|
||||||
|
const targetWindow = windows.find((w) =>
|
||||||
|
w.tabs.some((t) => t.id === message.tabId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetWindow) {
|
||||||
|
console.error("Could not find window containing the inspected tab");
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
error: "Could not find window containing the inspected tab",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture screenshot of the window containing our tab
|
||||||
|
chrome.tabs.captureVisibleTab(
|
||||||
|
targetWindow.id,
|
||||||
|
{ format: "png" },
|
||||||
|
(dataUrl) => {
|
||||||
|
// Ignore DevTools panel capture error if it occurs
|
||||||
|
if (
|
||||||
|
chrome.runtime.lastError &&
|
||||||
|
!chrome.runtime.lastError.message.includes("devtools://")
|
||||||
|
) {
|
||||||
|
console.error(
|
||||||
|
"Error capturing screenshot:",
|
||||||
|
chrome.runtime.lastError
|
||||||
|
);
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
error: chrome.runtime.lastError.message,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send screenshot data to browser connector using configured settings
|
||||||
|
const serverUrl = `http://${settings.serverHost}:${settings.serverPort}/screenshot`;
|
||||||
|
console.log(`Sending screenshot to ${serverUrl}`);
|
||||||
|
|
||||||
|
fetch(serverUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
data: dataUrl,
|
||||||
|
path: message.screenshotPath,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((result) => {
|
||||||
|
if (result.error) {
|
||||||
|
console.error("Error from server:", result.error);
|
||||||
|
sendResponse({ success: false, error: result.error });
|
||||||
|
} else {
|
||||||
|
console.log("Screenshot saved successfully:", result.path);
|
||||||
|
// Send success response even if DevTools capture failed
|
||||||
|
sendResponse({
|
||||||
|
success: true,
|
||||||
|
path: result.path,
|
||||||
|
title: tab.title || "Current Tab",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error sending screenshot data:", error);
|
||||||
|
sendResponse({
|
||||||
|
success: false,
|
||||||
|
error: error.message || "Failed to save screenshot",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -173,12 +173,20 @@ function processJsonString(jsonString, maxLength) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper to send logs to browser-connector
|
// Helper to send logs to browser-connector
|
||||||
function sendToBrowserConnector(logData) {
|
async function sendToBrowserConnector(logData) {
|
||||||
if (!logData) {
|
if (!logData) {
|
||||||
console.error("No log data provided to sendToBrowserConnector");
|
console.error("No log data provided to sendToBrowserConnector");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First, ensure we're connecting to the right server
|
||||||
|
if (!(await validateServerIdentity())) {
|
||||||
|
console.error(
|
||||||
|
"Cannot send logs: Not connected to a valid browser tools server"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Sending log data to browser connector:", {
|
console.log("Sending log data to browser connector:", {
|
||||||
type: logData.type,
|
type: logData.type,
|
||||||
timestamp: logData.timestamp,
|
timestamp: logData.timestamp,
|
||||||
@ -276,6 +284,37 @@ function sendToBrowserConnector(logData) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate server identity before connecting
|
||||||
|
async function validateServerIdentity() {
|
||||||
|
try {
|
||||||
|
const serverUrl = `http://${settings.serverHost}:${settings.serverPort}/.identity`;
|
||||||
|
|
||||||
|
// Check if the server is our browser-tools-server
|
||||||
|
const response = await fetch(serverUrl, {
|
||||||
|
signal: AbortSignal.timeout(2000), // 2 second timeout
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Invalid server response: ${response.status}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = await response.json();
|
||||||
|
|
||||||
|
// Validate the server signature
|
||||||
|
if (identity.signature !== "mcp-browser-connector-24x7") {
|
||||||
|
console.error("Invalid server signature - not the browser tools server");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If reached here, the server is valid
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error validating server identity:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function to clear logs on the server
|
// Function to clear logs on the server
|
||||||
function wipeLogs() {
|
function wipeLogs() {
|
||||||
console.log("Wiping all logs...");
|
console.log("Wiping all logs...");
|
||||||
@ -553,11 +592,21 @@ let ws = null;
|
|||||||
let wsReconnectTimeout = null;
|
let wsReconnectTimeout = null;
|
||||||
const WS_RECONNECT_DELAY = 5000; // 5 seconds
|
const WS_RECONNECT_DELAY = 5000; // 5 seconds
|
||||||
|
|
||||||
function setupWebSocket() {
|
async function setupWebSocket() {
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate server identity before connecting
|
||||||
|
if (!(await validateServerIdentity())) {
|
||||||
|
console.error(
|
||||||
|
"Cannot establish WebSocket: Not connected to a valid browser tools server"
|
||||||
|
);
|
||||||
|
// Try again after delay
|
||||||
|
setTimeout(setupWebSocket, WS_RECONNECT_DELAY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const wsUrl = `ws://${settings.serverHost}:${settings.serverPort}/extension-ws`;
|
const wsUrl = `ws://${settings.serverHost}:${settings.serverPort}/extension-ws`;
|
||||||
console.log(`Connecting to WebSocket at ${wsUrl}`);
|
console.log(`Connecting to WebSocket at ${wsUrl}`);
|
||||||
|
|
||||||
|
|||||||
@ -139,19 +139,28 @@ async function testConnection(host, port) {
|
|||||||
statusText.textContent = "Testing connection...";
|
statusText.textContent = "Testing connection...";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`http://${host}:${port}/.port`, {
|
// Use the identity endpoint instead of .port for more reliable validation
|
||||||
|
const response = await fetch(`http://${host}:${port}/.identity`, {
|
||||||
signal: AbortSignal.timeout(5000), // 5 second timeout
|
signal: AbortSignal.timeout(5000), // 5 second timeout
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const responsePort = await response.text();
|
const identity = await response.json();
|
||||||
|
|
||||||
|
// Verify this is actually our server by checking the signature
|
||||||
|
if (identity.signature !== "mcp-browser-connector-24x7") {
|
||||||
|
statusIcon.className = "status-indicator status-disconnected";
|
||||||
|
statusText.textContent = `Connection failed: Found a server at ${host}:${port} but it's not the Browser Tools server`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
statusIcon.className = "status-indicator status-connected";
|
statusIcon.className = "status-indicator status-connected";
|
||||||
statusText.textContent = `Connected successfully to server at ${host}:${port}`;
|
statusText.textContent = `Connected successfully to ${identity.name} v${identity.version} at ${host}:${port}`;
|
||||||
|
|
||||||
// Update settings if different port was discovered
|
// Update settings if different port was discovered
|
||||||
if (parseInt(responsePort, 10) !== port) {
|
if (parseInt(identity.port, 10) !== port) {
|
||||||
console.log(`Detected different port: ${responsePort}`);
|
console.log(`Detected different port: ${identity.port}`);
|
||||||
settings.serverPort = parseInt(responsePort, 10);
|
settings.serverPort = parseInt(identity.port, 10);
|
||||||
serverPortInput.value = settings.serverPort;
|
serverPortInput.value = settings.serverPort;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
@ -218,24 +227,33 @@ discoverServerButton.addEventListener("click", async () => {
|
|||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 1000); // 1 second timeout per attempt
|
const timeoutId = setTimeout(() => controller.abort(), 1000); // 1 second timeout per attempt
|
||||||
|
|
||||||
const response = await fetch(`http://${host}:${port}/.port`, {
|
// Use identity endpoint instead of .port for more reliable server validation
|
||||||
|
const response = await fetch(`http://${host}:${port}/.identity`, {
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const responsePort = await response.text();
|
const identity = await response.json();
|
||||||
|
|
||||||
|
// Verify this is actually our server by checking the signature
|
||||||
|
if (identity.signature !== "mcp-browser-connector-24x7") {
|
||||||
|
console.log(
|
||||||
|
`Found a server at ${host}:${port} but it's not the Browser Tools server`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Update settings with discovered server
|
// Update settings with discovered server
|
||||||
settings.serverHost = host;
|
settings.serverHost = host;
|
||||||
settings.serverPort = parseInt(responsePort, 10);
|
settings.serverPort = parseInt(identity.port, 10);
|
||||||
serverHostInput.value = settings.serverHost;
|
serverHostInput.value = settings.serverHost;
|
||||||
serverPortInput.value = settings.serverPort;
|
serverPortInput.value = settings.serverPort;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
statusIcon.className = "status-indicator status-connected";
|
statusIcon.className = "status-indicator status-connected";
|
||||||
statusText.textContent = `Discovered server at ${host}:${responsePort}`;
|
statusText.textContent = `Discovered ${identity.name} v${identity.version} at ${host}:${identity.port}`;
|
||||||
|
|
||||||
// Stop searching once found
|
// Stop searching once found
|
||||||
return;
|
return;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user