Files
makelore/electron/agent-browser/cdp-guard.ts
brother7 e97a7ce9df feat: 增加共享 Agent Browser 调试能力
需求:在 AI 编程会话中让用户与 Agent 共享同一浏览器页面,并查看控制台与网络信息。

实现:新增沙箱浏览器内核、Host API/渲染器面板、OpenCode 工具接入及安全边界测试。
2026-07-31 14:53:36 +08:00

109 lines
3.1 KiB
TypeScript

import { AgentBrowserFault } from './fault';
const BLOCKED_METHODS = new Set([
'DOM.getFileInfo',
'DOM.setFileInputFiles',
'Page.crash',
'Page.setDownloadBehavior',
'Security.handleCertificateError',
'Security.setDisableNetworkAccessForOrigins',
'Security.setIgnoreCertificateErrors',
'Security.setOverrideCertificateErrors',
]);
const BLOCKED_DOMAINS = [
'Cast.',
'DeviceAccess.',
'Extensions.',
'Tethering.',
] as const;
const URL_FIELDS_BY_METHOD = new Map<string, string[]>([
['Fetch.continueRequest', ['url']],
['Network.continueInterceptedRequest', ['url']],
['Network.loadNetworkResource', ['url']],
['Page.navigate', ['url']],
]);
function hasExternalTargetReference(params: Record<string, unknown> | undefined): boolean {
return Boolean(params && (
typeof params.targetId === 'string'
|| typeof params.browserContextId === 'string'
));
}
function isAllowedPageUrl(value: string): boolean {
try {
const url = new URL(value);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}
export class AgentBrowserCdpGuard {
assertAllowed(
method: string,
params: Record<string, unknown> | undefined,
sessionRef: string | undefined,
childSessions: ReadonlySet<string>,
ioHandles: ReadonlySet<string>,
): void {
const blockedHostDomain = (
(method.startsWith('Browser.') && method !== 'Browser.getVersion')
|| method.startsWith('SystemInfo.')
|| method.startsWith('Memory.')
|| method.startsWith('Tracing.')
|| BLOCKED_DOMAINS.some((domain) => method.startsWith(domain))
);
if (BLOCKED_METHODS.has(method) || blockedHostDomain) {
throw new AgentBrowserFault(
'CDP_METHOD_BLOCKED',
`CDP method ${method} 可能影响 Makelore 宿主,已阻止。`,
false,
);
}
if (method.startsWith('Target.')) {
throw new AgentBrowserFault(
'TARGET_DENIED',
'Target 域由开发浏览器托管,不能直接操作其他 Electron 页面。',
false,
);
}
if (hasExternalTargetReference(params)) {
throw new AgentBrowserFault(
'TARGET_DENIED',
'不能指定开发浏览器之外的 CDP target。',
false,
);
}
for (const field of URL_FIELDS_BY_METHOD.get(method) ?? []) {
const value = params?.[field];
if (typeof value === 'string' && !isAllowedPageUrl(value)) {
throw new AgentBrowserFault(
'CDP_METHOD_BLOCKED',
`${method} 只允许访问 HTTP 或 HTTPS 地址。`,
false,
);
}
}
if (sessionRef && !childSessions.has(sessionRef)) {
throw new AgentBrowserFault(
'TARGET_DENIED',
'CDP session 不属于当前开发浏览器。',
false,
);
}
if (method.startsWith('IO.')) {
const handle = params?.handle;
if (typeof handle !== 'string' || !ioHandles.has(handle)) {
throw new AgentBrowserFault(
'TARGET_DENIED',
'IO handle 不属于当前开发浏览器会话。',
false,
);
}
}
}
}