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([ ['Fetch.continueRequest', ['url']], ['Network.continueInterceptedRequest', ['url']], ['Network.loadNetworkResource', ['url']], ['Page.navigate', ['url']], ]); function hasExternalTargetReference(params: Record | 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 | undefined, sessionRef: string | undefined, childSessions: ReadonlySet, ioHandles: ReadonlySet, ): 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, ); } } } }