camoufox-mcp-server
Version:
MCP server for browser automation using Camoufox - a privacy-focused Firefox fork with advanced anti-detection features
106 lines (105 loc) • 3.9 kB
JavaScript
import { validateTargetUrl } from "./policy.js";
import { ALLOW_UNSAFE_OPTIONS, DENIED_BROWSER_ARG_FLAGS, DENIED_FIREFOX_PREF_KEYS, DENIED_FIREFOX_PREF_PREFIXES } from "./config.js";
import { describeError, getProxyServer } from "./utils.js";
export async function validateProxyConfig(proxy) {
const server = getProxyServer(proxy);
if (!server) {
return;
}
try {
await validateTargetUrl(server);
}
catch (error) {
throw new Error(`Proxy server is not allowed. ${describeError(error)}`, { cause: error });
}
}
export async function validateBrowserOptionsInput(input) {
await validateProxyConfig(input.proxy);
if (!ALLOW_UNSAFE_OPTIONS && hasUnsafeBrowserOptions(input.args, input.firefox_user_prefs, input.exclude_addons)) {
throw new Error("Unsafe browser options are disabled by server policy. Set CAMOUFOX_MCP_ALLOW_UNSAFE_OPTIONS=1 to allow args, firefox_user_prefs, or exclude_addons.");
}
const deniedUnsafeOption = findDeniedUnsafeBrowserOption(input.args, input.firefox_user_prefs);
if (deniedUnsafeOption) {
throw new Error(`Unsafe browser option is denied by server policy: ${deniedUnsafeOption}.`);
}
}
export function hasUnsafeBrowserOptions(args, firefoxUserPrefs, excludeAddons) {
return Boolean((args?.length ?? 0) > 0
|| Object.keys(firefoxUserPrefs ?? {}).length > 0
|| (excludeAddons?.length ?? 0) > 0);
}
export function normalizedArgFlag(arg) {
const match = arg.trim().match(/^(-{1,2}[^\s=]+)/);
return match?.[1]?.toLowerCase();
}
export function findDeniedBrowserArg(args) {
for (let index = 0; index < (args?.length ?? 0); index += 1) {
const flag = normalizedArgFlag(args?.[index] ?? "");
if (!flag) {
continue;
}
if (DENIED_BROWSER_ARG_FLAGS.has(flag)) {
return flag;
}
}
return undefined;
}
export function findDeniedFirefoxPref(firefoxUserPrefs) {
for (const key of Object.keys(firefoxUserPrefs ?? {})) {
const normalizedKey = key.toLowerCase();
if (DENIED_FIREFOX_PREF_KEYS.has(normalizedKey)
|| DENIED_FIREFOX_PREF_PREFIXES.some((prefix) => normalizedKey.startsWith(prefix))) {
return key;
}
}
return undefined;
}
export function findDeniedUnsafeBrowserOption(args, firefoxUserPrefs) {
const deniedArg = findDeniedBrowserArg(args);
if (deniedArg) {
return `args contains ${deniedArg}`;
}
const deniedPref = findDeniedFirefoxPref(firefoxUserPrefs);
if (deniedPref) {
return `firefox_user_prefs contains ${deniedPref}`;
}
return undefined;
}
export async function validateCommonBrowserInput(input) {
const targetUrl = await validateTargetUrl(input.url);
await validateBrowserOptionsInput(input);
return targetUrl;
}
export function buildCamoufoxOptions(input, selectedOS, headlessMode) {
return {
os: [selectedOS],
headless: headlessMode,
humanize: input.humanize,
geoip: input.geoip,
ublock: true,
block_webgl: input.block_webgl,
block_images: input.block_images,
block_webrtc: input.block_webrtc,
disable_coop: input.disable_coop,
locale: input.locale,
viewport: input.viewport ? {
width: input.viewport.width,
height: input.viewport.height,
} : undefined,
proxy: input.proxy,
enable_cache: input.enable_cache,
firefox_user_prefs: input.firefox_user_prefs,
exclude_addons: input.exclude_addons,
window: input.window,
args: input.args,
};
}
export function browserContextOptions(input) {
return {
serviceWorkers: "block",
viewport: input.viewport ? {
width: input.viewport.width,
height: input.viewport.height,
} : undefined,
};
}