import { mkdtemp, mkdir, readFile, rm } from 'node:fs/promises'; import { generateKeyPairSync, randomBytes, webcrypto } from 'node:crypto'; import { createServer } from 'node:http'; import { createRequire } from 'node:module'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { chromium } from 'playwright-core'; import { WebSocketServer } from 'ws'; import { resolveChromiumPath } from './resolve-chromium.mjs'; const root = resolve(import.meta.dirname, '..'); const require = createRequire(import.meta.url); const NodeJSEncrypt = require('jsencrypt'); const NodeSMCrypto = require('sm-crypto'); const NodeForge = require('node-forge'); const jsencryptBrowserPath = resolve(root, 'node_modules/jsencrypt/bin/jsencrypt.min.js'); const sm2BrowserPath = resolve(root, 'node_modules/sm-crypto/dist/sm2.js'); const sm3BrowserPath = resolve(root, 'node_modules/sm-crypto/dist/sm3.js'); const sm4BrowserPath = resolve(root, 'node_modules/sm-crypto/dist/sm4.js'); const nodeForgeBrowserPath = resolve(root, 'node_modules/node-forge/dist/forge.min.js'); const extensionPath = resolve(root, process.env.EXTENSION_PATH || '.output/chrome-mv3'); const extensionManifest = JSON.parse(await readFile(resolve(extensionPath, 'manifest.json'), 'utf8')); const shouldEnableUserScripts = process.env.ENABLE_USER_SCRIPTS !== '0' && extensionManifest.permissions?.includes('userScripts'); const artifacts = resolve(root, '.artifacts/ui'); const executablePath = await resolveChromiumPath(); const userDataDir = await mkdtemp(join(tmpdir(), 'yakit-extension-')); await mkdir(artifacts, { recursive: true }); const cryptoLabAESBytes = Buffer.from('00112233445566778899aabbccddeeff102132435465768798a9bacbdcedfe0f', 'hex'); const cryptoLabHMACBytes = Buffer.from('ffeeddccbbaa998877665544332211000f1e2d3c4b5a69788796a5b4c3d2e1f0', 'hex'); const cryptoLabAESKey = webcrypto.subtle.importKey('raw', cryptoLabAESBytes, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']); const cryptoLabHMACKey = webcrypto.subtle.importKey('raw', cryptoLabHMACBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']); const rsaLabKeyPair = generateKeyPairSync('rsa', { modulusLength: 1024 }); const rsaLabPublicKey = rsaLabKeyPair.publicKey.export({ type: 'spki', format: 'pem' }); const rsaLabPrivateKey = rsaLabKeyPair.privateKey.export({ type: 'pkcs8', format: 'pem' }); const rsaLabPublicJWK = rsaLabKeyPair.publicKey.export({ format: 'jwk' }); const rsaLabPublicModulusHex = Buffer.from(rsaLabPublicJWK.n, 'base64url').toString('hex'); const rsaLabDecryptor = new NodeJSEncrypt(); rsaLabDecryptor.setPrivateKey(rsaLabPrivateKey); const smLabKeyPair = NodeSMCrypto.sm2.generateKeyPairHex(); const smLabSM4Key = '0123456789abcdeffedcba9876543210'; const smLabSM4IV = 'fedcba98765432100123456789abcdef'; const forgeLabAESKey = '00112233445566778899aabbccddeeff'; const forgeLabAESIV = '102132435465768798a9bacbdcedfe0f'; const forgeLabPrivateKey = NodeForge.pki.privateKeyFromPem(rsaLabPrivateKey); const closureHoldoutSeed = randomBytes(6).toString('hex'); const closureModulePath = `/assets/opaque-${closureHoldoutSeed}.mjs`; const closureSubmitPath = `/gateway/opaque-${closureHoldoutSeed}`; const closurePayloadField = `blob_${closureHoldoutSeed}`; const closureDigestField = `proof_${closureHoldoutSeed}`; const closureFunctionName = `build_${closureHoldoutSeed}`; const closureSenderName = `send_${closureHoldoutSeed}`; const closureInitialMarker = `module-recording-${closureHoldoutSeed}`; const closureReplayMarker = `module-replay-${closureHoldoutSeed}`; const WASM_XOR_MASK = 23; function decryptRSALabValue(value) { const plaintext = rsaLabDecryptor.decrypt(value); if (typeof plaintext !== 'string') throw new Error('JSEncrypt server could not decrypt the RSA ciphertext'); return plaintext; } async function decryptCryptoLabResponse(envelope) { const plaintext = await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv: Buffer.from(envelope.iv, 'base64'), additionalData: Buffer.from(`response:${envelope.nonce}`), }, await cryptoLabAESKey, Buffer.from(envelope.ciphertext, 'base64')); return JSON.parse(Buffer.from(plaintext).toString('utf8')); } const server = createServer(async (request, response) => { if (request.url?.startsWith(closureModulePath)) { response.setHeader('content-type', 'text/javascript; charset=utf-8'); response.end(`const encoder = new TextEncoder(); const wasmBytes = Uint8Array.from([0,97,115,109,1,0,0,0,1,6,1,96,1,127,1,127,3,2,1,0,7,7,1,3,109,105,120,0,0,10,9,1,7,0,32,0,65,${WASM_XOR_MASK},115,11]); const { instance } = await WebAssembly.instantiate(wasmBytes); const toBase64 = (value) => { let binary = ''; for (const byte of value) binary += String.fromCharCode(byte); return btoa(binary); }; async function ${closureFunctionName}(payload) { const plaintext = encoder.encode(JSON.stringify(payload)); const transformed = Uint8Array.from(plaintext, (byte) => instance.exports.mix(byte)); const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', transformed)); return { [${JSON.stringify(closurePayloadField)}]: toBase64(transformed), [${JSON.stringify(closureDigestField)}]: toBase64(digest), }; } async function ${closureSenderName}() { const marker = document.querySelector('#opaque-module-input').value; const envelope = await ${closureFunctionName}({ marker, nested: { seed: ${JSON.stringify(closureHoldoutSeed)} } }); const response = await fetch(${JSON.stringify(closureSubmitPath)}, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(envelope), }); const result = await response.json(); if (!result.ok) throw new Error(result.error || 'opaque module rejected'); document.querySelector('#opaque-module-result').textContent = result.marker; } document.querySelector('#opaque-module-submit').addEventListener('click', () => void ${closureSenderName}()); document.querySelector('#opaque-module-lab').dataset.ready = 'true';`); return; } if (request.url?.startsWith(closureSubmitPath)) { const chunks = []; for await (const chunk of request) chunks.push(chunk); try { const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8')); const transformed = Buffer.from(envelope[closurePayloadField], 'base64'); const plaintextBytes = Buffer.from(transformed.map((byte) => byte ^ WASM_XOR_MASK)); const payload = JSON.parse(plaintextBytes.toString('utf8')); const digest = Buffer.from(await webcrypto.subtle.digest('SHA-256', transformed)).toString('base64'); if (envelope[closureDigestField] !== digest) throw new Error('opaque module digest mismatch'); if (typeof payload.marker !== 'string' || payload.nested?.seed !== closureHoldoutSeed) { throw new Error('opaque module payload mismatch'); } response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: true, marker: payload.marker })); } catch (error) { response.statusCode = 400; response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: false, error: error.message })); } return; } if (request.url?.startsWith('/opaque-worker.js')) { response.setHeader('content-type', 'text/javascript; charset=utf-8'); response.end(`self.addEventListener('message', (event) => { const plaintext = JSON.stringify(event.data); const bytes = new TextEncoder().encode(plaintext); let binary = ''; for (const byte of bytes) binary += String.fromCharCode(byte); self.postMessage({ sealed: btoa(binary), byteLength: bytes.byteLength }); });`); return; } if (request.url?.startsWith('/opaque-worker-submit')) { const chunks = []; for await (const chunk of request) chunks.push(chunk); try { const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8')); const plaintext = JSON.parse(Buffer.from(envelope.sealed, 'base64').toString('utf8')); if (plaintext.marker !== 'worker-boundary-holdout-811') throw new Error('unexpected Worker plaintext'); response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: true, marker: plaintext.marker })); } catch (error) { response.statusCode = 400; response.end(JSON.stringify({ ok: false, error: error.message })); } return; } if (request.url?.startsWith('/beacon-boundary')) { response.statusCode = 204; response.end(); return; } if (request.url?.startsWith('/semantic-adapter-submit')) { const chunks = []; for await (const chunk of request) chunks.push(chunk); try { const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8')); const smPlaintext = NodeSMCrypto.sm2.doDecrypt(envelope.sm2Ciphertext, smLabKeyPair.privateKey, 1); if (smPlaintext !== envelope.expectedSMPlaintext) throw new Error('SM2 server decryption mismatch'); if (!NodeSMCrypto.sm2.doVerifySignature( envelope.expectedSMPlaintext, envelope.sm2Signature, smLabKeyPair.publicKey, )) throw new Error('SM2 server signature verification failed'); if (NodeSMCrypto.sm3(envelope.expectedSMPlaintext) !== envelope.sm3Digest) throw new Error('SM3 digest mismatch'); const sm4Plaintext = NodeSMCrypto.sm4.decrypt(envelope.sm4Ciphertext, smLabSM4Key, { mode: 'cbc', iv: smLabSM4IV, }); if (sm4Plaintext !== envelope.expectedSMPlaintext) throw new Error('SM4 server decryption mismatch'); const decipher = NodeForge.cipher.createDecipher('AES-CBC', NodeForge.util.hexToBytes(forgeLabAESKey)); decipher.start({ iv: NodeForge.util.hexToBytes(forgeLabAESIV) }); decipher.update(NodeForge.util.createBuffer(NodeForge.util.hexToBytes(envelope.forgeAesCiphertext))); if (!decipher.finish()) throw new Error('node-forge AES server decryption failed'); if (decipher.output.getBytes() !== envelope.expectedForgePlaintext) throw new Error('node-forge AES plaintext mismatch'); const forgeRsaPlaintext = forgeLabPrivateKey.decrypt( NodeForge.util.decode64(envelope.forgeRsaCiphertext), 'RSAES-PKCS1-V1_5', ); if (forgeRsaPlaintext !== envelope.expectedForgePlaintext) throw new Error('node-forge RSA plaintext mismatch'); const digest = NodeForge.md.sha256.create(); digest.update(envelope.expectedForgePlaintext, 'utf8'); if (digest.digest().toHex() !== envelope.forgeDigest) throw new Error('node-forge digest mismatch'); const hmac = NodeForge.hmac.create(); hmac.start('sha256', NodeForge.util.hexToBytes(forgeLabAESKey)); hmac.update(envelope.expectedForgePlaintext); if (hmac.digest().toHex() !== envelope.forgeHmac) throw new Error('node-forge HMAC mismatch'); response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: true })); } catch (error) { response.statusCode = 400; response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: false, error: error.message })); } return; } if (request.url?.startsWith('/proxy-rules')) { if (request.headers['if-none-match'] === '"yakit-e2e-rules-v1"') { response.statusCode = 304; response.end(); return; } response.setHeader('content-type', 'text/plain; charset=utf-8'); response.setHeader('etag', '"yakit-e2e-rules-v1"'); response.end('[AutoProxy 0.2]\n@@||allowed.subscription.test^\n||blocked.subscription.test^\n'); return; } if (request.url?.startsWith('/api/session')) { const chunks = []; request.on('data', (chunk) => chunks.push(chunk)); request.on('end', () => { response.setHeader('content-type', 'application/json; charset=utf-8'); response.setHeader('x-yakit-e2e-response', 'captured'); response.end(JSON.stringify({ ok: true, receivedBytes: Buffer.concat(chunks).length, userAgent: request.headers['user-agent'] || '', })); }); return; } if (request.url?.startsWith('/encrypt/rsa.php')) { const chunks = []; for await (const chunk of request) chunks.push(chunk); try { const ciphertext = new URLSearchParams(Buffer.concat(chunks).toString('utf8')).get('data'); if (!ciphertext) throw new Error('missing RSA form field data'); const plaintext = decryptRSALabValue(ciphertext); response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: true, plaintext })); } catch (error) { response.statusCode = 400; response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: false, error: error.message })); } return; } if (request.url?.startsWith('/crypto-lab/submit')) { const chunks = []; for await (const chunk of request) chunks.push(chunk); try { const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8')); const signedValue = [envelope.timestamp, envelope.nonce, envelope.iv, envelope.ciphertext].join('.'); const signatureValid = await webcrypto.subtle.verify( 'HMAC', await cryptoLabHMACKey, Buffer.from(envelope.signature, 'base64'), Buffer.from(signedValue), ); if (!signatureValid) throw new Error('invalid signature'); const plaintext = await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv: Buffer.from(envelope.iv, 'base64'), additionalData: Buffer.from(`${envelope.timestamp}:${envelope.nonce}`), }, await cryptoLabAESKey, Buffer.from(envelope.ciphertext, 'base64')); const payload = JSON.parse(Buffer.from(plaintext).toString('utf8')); if (payload.timestamp !== envelope.timestamp || payload.nonce !== envelope.nonce || typeof payload.password !== 'string') { throw new Error('invalid encrypted payload'); } const responsePayload = Buffer.from(JSON.stringify({ ok: true, account: payload.account, password: payload.password, nonce: payload.nonce, })); const responseIV = randomBytes(12); const responseCiphertext = await webcrypto.subtle.encrypt({ name: 'AES-GCM', iv: responseIV, additionalData: Buffer.from(`response:${payload.nonce}`), }, await cryptoLabAESKey, responsePayload); response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ nonce: payload.nonce, iv: responseIV.toString('base64'), ciphertext: Buffer.from(responseCiphertext).toString('base64'), })); } catch (error) { response.statusCode = 400; response.setHeader('content-type', 'application/json; charset=utf-8'); response.end(JSON.stringify({ ok: false, error: error.message })); } return; } if (request.url?.startsWith('/frame-')) { const cross = request.url.startsWith('/frame-cross'); response.setHeader('content-type', 'text/html; charset=utf-8'); response.end(`${cross ? 'Cross Origin Account Frame' : 'Same Origin Billing Frame'}

${cross ? 'Cross account' : 'Billing'}

`); return; } if (request.url?.startsWith('/strict-csp')) { response.setHeader('content-type', 'text/html; charset=utf-8'); response.setHeader('content-security-policy', "default-src 'none'; script-src 'none'; style-src 'none'; frame-src 'none'"); response.end('Strict CSP Page

Strict CSP Page

'); return; } if (request.url?.startsWith('/recording-complete')) { response.setHeader('content-type', 'text/html; charset=utf-8'); response.end('Recording Complete Dashboard

Recording Complete Dashboard

The login navigation completed in the same browser tab.

'); return; } response.setHeader('content-type', 'text/html; charset=utf-8'); response.setHeader('set-cookie', 'yakit_e2e_session=authenticated; Path=/; HttpOnly; SameSite=Lax'); const port = server.address()?.port; response.end(` Authenticated Security Console

Authenticated Security Console

Local page for extension UI and main-world execution verification.

`); }); const pageSocketServer = new WebSocketServer({ server, path: '/page-socket' }); pageSocketServer.on('connection', (socket) => socket.on('message', (message) => socket.send(`echo:${message.toString()}`))); await new Promise((resolveListen) => server.listen(0, '127.0.0.1', resolveListen)); const address = server.address(); const testUrl = `http://127.0.0.1:${address.port}/authenticated`; const insecureTestUrl = `http://yakit-insecure.test:${address.port}/insecure`; function installPageCryptoFixtures(publicKey) { window.CryptoJS = { SHA256(value) { return { sigBytes: 32, toString: () => `sha256:${value}` }; }, }; if (typeof window.JSEncrypt !== 'function') throw new Error('The real JSEncrypt browser bundle did not install'); window.__yakitRsa = new window.JSEncrypt(); window.__yakitRsa.setPublicKey(publicKey); window.__yakitObserverOriginals = { fetch: window.fetch, xhrOpen: XMLHttpRequest.prototype.open, xhrSend: XMLHttpRequest.prototype.send, webSocket: window.WebSocket, worker: window.Worker, workerPostMessage: window.Worker?.prototype.postMessage, messageChannel: window.MessageChannel, messagePortPostMessage: window.MessagePort?.prototype.postMessage, sendBeacon: Navigator.prototype.sendBeacon, digest: Object.getPrototypeOf(crypto.subtle).digest, cryptoJsSha256: window.CryptoJS.SHA256, jsencryptEncrypt: window.JSEncrypt.prototype.encrypt, sm2Encrypt: window.sm2?.doEncrypt, sm3: window.sm3, sm4Encrypt: window.sm4?.encrypt, forgeCreateCipher: window.forge?.cipher.createCipher, forgePublicKeyFromPem: window.forge?.pki.publicKeyFromPem, forgeSha256Create: window.forge?.md.sha256.create, forgeHmacCreate: window.forge?.hmac.create, }; } const bridgeHTTPServer = createServer(); const pairingServer = new WebSocketServer({ noServer: true }); const bridgeServer = new WebSocketServer({ noServer: true }); bridgeHTTPServer.on('upgrade', (request, socket, head) => { const pathname = new URL(request.url || '/', 'http://127.0.0.1').pathname; const target = pathname === '/pairing' ? pairingServer : pathname === '/extension' ? bridgeServer : undefined; if (!target) { socket.destroy(); return; } target.handleUpgrade(request, socket, head, (webSocket) => target.emit('connection', webSocket, request)); }); await new Promise((resolveListen) => bridgeHTTPServer.listen(0, '127.0.0.1', resolveListen)); const bridgeAddress = bridgeHTTPServer.address(); const bridgeEndpoint = `ws://127.0.0.1:${bridgeAddress.port}/extension`; const bridgeProtocolVersion = 3; const engineIdentityId = 'e2e-engine-identity'; const engineInstanceId = 'e2e-engine-instance'; const engineKeys = await webcrypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']); const engineJWKRaw = await webcrypto.subtle.exportKey('jwk', engineKeys.publicKey); const enginePublicKey = { kty: 'EC', crv: 'P-256', x: engineJWKRaw.x, y: engineJWKRaw.y }; let pairedClient; function bytesToBase64URL(value) { return Buffer.from(value).toString('base64url'); } async function sha256(value) { return new Uint8Array(await webcrypto.subtle.digest('SHA-256', Buffer.from(value))); } async function signEngine(value) { return bytesToBase64URL(await webcrypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, engineKeys.privateKey, Buffer.from(value))); } function engineChallengePayload(challenge, timestamp) { return ['yak-browser-bridge-v3', 'engine-challenge', engineIdentityId, engineInstanceId, challenge, String(timestamp)].join('\n'); } function clientAuthPayload(origin, challenge, auth) { return [ 'yak-browser-bridge-v3', 'client-auth', origin, engineIdentityId, engineInstanceId, challenge, auth.installationId || '', auth.client || '', auth.version || '', [...(auth.capabilities || [])].sort().join(','), auth.taskId || '', auth.grantId || '', auth.resumeSessionId || '', ].join('\n'); } async function verifyClientAuth(origin, challenge, auth) { if (!pairedClient || pairedClient.installationId !== auth.installationId) return false; const key = await webcrypto.subtle.importKey('jwk', pairedClient.publicKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']); return await webcrypto.subtle.verify( { name: 'ECDSA', hash: 'SHA-256' }, key, Buffer.from(auth.signature, 'base64url'), Buffer.from(clientAuthPayload(origin, challenge, auth)), ); } pairingServer.on('connection', (socket, request) => socket.once('message', async (raw) => { try { const pairing = JSON.parse(raw.toString()); if (pairing.type !== 'pair_request' || pairing.protocolVersion !== bridgeProtocolVersion) throw new Error('invalid pairing request'); const requestId = 'e2e-pairing-request'; const serverNonce = bytesToBase64URL(randomBytes(32)); const transcript = [ 'yak-browser-pairing-v1', engineIdentityId, requestId, request.headers.origin, pairing.installationId, pairing.nonce, serverNonce, pairing.publicKey.kty, pairing.publicKey.crv, pairing.publicKey.x, pairing.publicKey.y, ].join('\n'); const digest = await sha256(transcript); const code = String(Buffer.from(digest.subarray(0, 8)).readBigUInt64BE() % 1_000_000n).padStart(6, '0'); pairedClient = { installationId: pairing.installationId, publicKey: pairing.publicKey, origin: request.headers.origin }; socket.send(JSON.stringify({ type: 'pair_pending', protocolVersion: bridgeProtocolVersion, requestId, serverNonce, engineIdentityId, code, expiresAt: Date.now() + 60_000, publicKey: enginePublicKey, })); setTimeout(() => socket.send(JSON.stringify({ type: 'pair_approved', requestId, deviceId: 'e2e-browser-device', engineIdentityId, publicKey: enginePublicKey, })), 50); } catch (error) { socket.send(JSON.stringify({ type: 'pair_error', message: error.message })); } })); let resolveWebFuzzerOpen; const webFuzzerOpenRequest = new Promise((resolveRequest) => { resolveWebFuzzerOpen = resolveRequest; }); let resolvePocGenerate; const pocGenerateRequest = new Promise((resolveRequest) => { resolvePocGenerate = resolveRequest; }); let resolveAnalysisPrepare; const analysisPrepareRequest = new Promise((resolveRequest) => { resolveAnalysisPrepare = resolveRequest; }); const bridgeConnection = new Promise((resolveConnection, rejectConnection) => { bridgeServer.once('connection', async (socket, request) => { const challenge = bytesToBase64URL(randomBytes(32)); const timestamp = Date.now(); socket.send(JSON.stringify({ type: 'challenge', protocolVersion: bridgeProtocolVersion, challenge, timestamp, engineIdentityId, engineInstanceId, publicKey: enginePublicKey, signature: await signEngine(engineChallengePayload(challenge, timestamp)), })); socket.once('message', (raw) => { void (async () => { const hello = JSON.parse(raw.toString()); if (hello.type !== 'auth' || hello.challenge !== challenge || !(await verifyClientAuth(request.headers.origin, challenge, hello))) { throw new Error('invalid Bridge v3 client authentication'); } socket.send(JSON.stringify({ type: 'hello_ack', protocolVersion: bridgeProtocolVersion, version: 'e2e-engine', capabilities: ['yakit.web_fuzzer.open', 'yakit.poc.generate', 'yakit.browser_request.prepare_analysis'], sessionId: 'e2e-session', engineIdentityId, engineInstanceId, connectionId: 'e2e-connection', taskId: hello.taskId, grantId: hello.grantId, resumed: Boolean(hello.resumeSessionId), })); socket.on('message', (requestRaw) => { try { const message = JSON.parse(requestRaw.toString()); if (message.type === 'ping') { socket.send(JSON.stringify({ type: 'pong', id: message.id, sequence: message.sequence, timestamp: message.timestamp, replyTimestamp: Date.now() })); return; } if (message.type !== 'request') return; if (message.method === 'yakit.web_fuzzer.open') { resolveWebFuzzerOpen(message); socket.send(JSON.stringify({ id: message.id, type: 'response', result: { pageId: 'e2e-fuzzer-page', tabName: message.params?.tabName || 'Browser Request' } })); } else if (message.method === 'yakit.poc.generate') { resolvePocGenerate(message); socket.send(JSON.stringify({ id: message.id, type: 'response', result: { language: 'yak', fileName: 'browser-e2e.yak', code: 'packet = codec.DecodeBase64("e2e")\nrsp, req, err = poc.HTTP(packet)' } })); } else if (message.method === 'yakit.browser_request.prepare_analysis') { resolveAnalysisPrepare(message); socket.send(JSON.stringify({ id: message.id, type: 'response', result: { request: { method: 'POST', scheme: 'http', host: '127.0.0.1', path: '/api/session', contentType: 'application/json', queryKeys: ['source'], headerNames: ['Cookie', 'X-Yakit-E2e'], cookieNames: ['yakit_e2e_session'], bodyKeys: ['marker'], bodyBytes: 38 }, signals: [{ location: 'header', name: 'Cookie', category: 'cookie' }], observations: message.params?.observations || [], valuePolicy: 'values omitted', recommendedChecks: ['authorization boundary'], }, })); } } catch { // The main E2E assertions report malformed Bridge traffic. } }); resolveConnection({ socket, hello }); })().catch((error) => { rejectConnection(error); }); }); }); }); function nextBridgeResponse(socket, id, timeoutMs = 30_000) { return new Promise((resolveResponse, rejectResponse) => { const timeout = setTimeout(() => { socket.off('message', onMessage); rejectResponse(new Error(`Bridge response timed out: ${id}`)); }, timeoutMs); const onMessage = (raw) => { try { const message = JSON.parse(raw.toString()); if (message.id !== id) return; clearTimeout(timeout); socket.off('message', onMessage); resolveResponse(message); } catch (error) { clearTimeout(timeout); socket.off('message', onMessage); rejectResponse(error); } }; socket.on('message', onMessage); }); } function nextBridgeEvent(socket, method) { return new Promise((resolveEvent, rejectEvent) => { const onMessage = (raw) => { try { const message = JSON.parse(raw.toString()); if (message.type !== 'event' || message.method !== method) return; socket.off('message', onMessage); resolveEvent(message); } catch (error) { socket.off('message', onMessage); rejectEvent(error); } }; socket.on('message', onMessage); }); } async function callBridge(socket, id, method, params) { const response = nextBridgeResponse(socket, id); socket.send(JSON.stringify({ id, type: 'request', method, params })); return await response; } const browserErrors = []; let context; try { context = await chromium.launchPersistentContext(userDataDir, { executablePath, headless: true, viewport: { width: 1280, height: 760 }, args: [ `--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`, '--host-resolver-rules=MAP yakit-insecure.test 127.0.0.1', '--no-first-run', '--no-default-browser-check', ], }); context.on('page', (page) => { page.on('pageerror', (error) => browserErrors.push(`${page.url()}: ${error.message}`)); }); let serviceWorker = context.serviceWorkers()[0]; if (!serviceWorker) serviceWorker = await context.waitForEvent('serviceworker', { timeout: 15_000 }); const extensionId = new URL(serviceWorker.url()).host; if (shouldEnableUserScripts) { const extensionsPage = await context.newPage(); await extensionsPage.goto(`chrome://extensions/?id=${extensionId}`); const userScriptsToggle = extensionsPage.locator('#allow-user-scripts cr-toggle'); await userScriptsToggle.waitFor({ state: 'visible', timeout: 10_000 }); const enabled = await userScriptsToggle.evaluate((toggle) => Boolean(toggle.checked)); if (!enabled) await userScriptsToggle.click(); await extensionsPage.close(); } const webPage = await context.newPage(); await webPage.setViewportSize({ width: 1280, height: 760 }); await webPage.goto(testUrl); const tabs = await serviceWorker.evaluate(async () => await chrome.tabs.query({})); const targetTab = tabs.find((tab) => tab.url === testUrl); if (!targetTab?.id) throw new Error('Could not resolve the test tab ID'); await webPage.evaluate(async () => { await new Promise((resolveDatabase, rejectDatabase) => { const open = indexedDB.open('yakit-e2e-auth', 1); open.onupgradeneeded = () => open.result.createObjectStore('sessions'); open.onerror = () => rejectDatabase(open.error); open.onsuccess = () => { const database = open.result; const transaction = database.transaction('sessions', 'readwrite'); transaction.objectStore('sessions').put({ authenticated: true }, 'account-1'); transaction.oncomplete = () => { database.close(); resolveDatabase(); }; transaction.onerror = () => rejectDatabase(transaction.error); }; }); const cache = await caches.open('yakit-e2e-session-cache'); await cache.put('/e2e-cached-session', new Response('cached')); history.pushState({ source: 'e2e' }, '', '/authenticated?spa=inventory'); }); const popup = await context.newPage(); await popup.setViewportSize({ width: 390, height: 600 }); await popup.goto(`chrome-extension://${extensionId}/popup.html`); await popup.locator('.popup-shell').waitFor(); await popup.getByText('Authenticated Security Console', { exact: true }).waitFor(); const popupBrandsLoaded = await popup.locator('.yak-mark, .yakit-mark').evaluateAll((images) => images.every((image) => image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0)); if (!popupBrandsLoaded) throw new Error('Popup brand assets did not load'); await popup.locator('.popup-rail').waitFor(); await popup.locator('.popup-brand-mark .yak-mark').waitFor(); await popup.locator('.popup-engine-status').waitFor(); await popup.screenshot({ path: resolve(artifacts, 'popup.png') }); await popup.locator('.popup-engine-status').hover(); await popup.getByText(/尚未配对引擎/).waitFor(); await popup.getByRole('button', { name: '代理', exact: true }).click(); await popup.locator('.popup-proxy-view').waitFor(); await popup.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'proxy.save', payload: { id: 'e2e-popup-proxy-a', name: 'E2E Proxy A', kind: 'fixed_servers', scheme: 'http', host: '127.0.0.1', port: 2080, bypass: [], } }); if (!response?.ok) throw new Error(response?.error || 'Unable to create E2E Proxy A'); }); const siteProxySelect = popup.getByLabel('当前站点代理出口'); await siteProxySelect.locator('option[value="e2e-popup-proxy-a"]').waitFor({ state: 'attached' }); await siteProxySelect.selectOption('e2e-popup-proxy-a'); await popup.locator('.popup-site-status.is-success').getByText('已应用 · E2E Proxy A', { exact: true }).waitFor(); if (await popup.getByRole('button', { name: '保存', exact: true }).count()) { throw new Error('Popup site route still requires an explicit save button'); } const popupSiteRoute = await popup.evaluate(async () => { const state = await chrome.runtime.sendMessage({ action: 'state.get' }); const preview = await chrome.runtime.sendMessage({ action: 'proxy.rules.preview', payload: { url: location.href.replace('chrome-extension://', 'https://') } }); return { state: state.data, preview }; }); const popupRouteRule = popupSiteRoute.state.proxyRules.find((rule) => rule.condition.type === 'host_exact' && rule.condition.value === '127.0.0.1'); if (popupSiteRoute.state.activeProxyId !== 'auto' || popupRouteRule?.proxyProfileId !== 'e2e-popup-proxy-a') { throw new Error(`Popup site route did not select arbitrary proxy A: ${JSON.stringify(popupSiteRoute)}`); } await popup.locator('.popup-global-notice').waitFor({ state: 'detached' }); await popup.waitForTimeout(120); const popupProxyBounds = await popup.evaluate(() => ({ scrollY: window.scrollY, viewportHeight: window.innerHeight, documentHeight: document.documentElement.scrollHeight, shellTop: document.querySelector('.popup-shell')?.getBoundingClientRect().top, shellBottom: document.querySelector('.popup-shell')?.getBoundingClientRect().bottom, })); if (popupProxyBounds.scrollY !== 0 || popupProxyBounds.shellTop !== 0 || popupProxyBounds.shellBottom > popupProxyBounds.viewportHeight) { throw new Error(`Popup proxy escaped its viewport: ${JSON.stringify(popupProxyBounds)}`); } await popup.screenshot({ path: resolve(artifacts, 'popup-proxy.png') }); const proxyOptionsPromise = context.waitForEvent('page'); await popup.getByRole('button', { name: '打开代理策略', exact: true }).click(); const proxyOptions = await proxyOptionsPromise; await proxyOptions.waitForLoadState('domcontentloaded'); if (!proxyOptions.url().includes('/options.html') || !proxyOptions.url().endsWith('#rules')) { throw new Error(`Popup global action did not open proxy options: ${proxyOptions.url()}`); } await proxyOptions.close(); await siteProxySelect.selectOption('__automatic__'); await popup.locator('.popup-site-status.is-success').getByText('已恢复自动判断', { exact: true }).waitFor(); const clearedPopupSiteRoute = await popup.evaluate(async () => (await chrome.runtime.sendMessage({ action: 'state.get' })).data); if (clearedPopupSiteRoute.proxyRules.some((rule) => rule.condition.type === 'host_exact' && rule.condition.value === '127.0.0.1')) { throw new Error(`Popup site route reset left an exact override: ${JSON.stringify(clearedPopupSiteRoute.proxyRules)}`); } await popup.getByRole('button', { name: '运行概览', exact: true }).click(); await popup.getByRole('button', { name: /Cookie Editor/ }).click(); await popup.locator('.popup-cookie-view').waitFor(); const popupSessionCookie = popup.locator('.popup-cookie-row').filter({ hasText: 'yakit_e2e_session' }); await popupSessionCookie.waitFor(); if (!(await popupSessionCookie.textContent()).includes('authenticated')) throw new Error('Popup Cookie Editor did not display the Cookie value directly'); if (await popupSessionCookie.getByRole('button', { name: /显示|隐藏/ }).count()) { throw new Error('Popup Cookie Editor still renders a value visibility control'); } await popup.getByRole('button', { name: '新增 Cookie' }).click(); const popupCookieEditor = popup.locator('.popup-cookie-editor'); await popupCookieEditor.locator('input').nth(0).fill('popup_e2e_cookie'); await popupCookieEditor.locator('input').nth(1).fill('popup-cookie-value'); await popup.getByRole('button', { name: '创建 Cookie' }).click(); const popupCreatedCookie = popup.locator('.popup-cookie-row').filter({ hasText: 'popup_e2e_cookie' }); await popupCreatedCookie.waitFor(); await popupCreatedCookie.getByRole('button', { name: '删除 popup_e2e_cookie' }).click(); await popupCreatedCookie.waitFor({ state: 'detached' }); await popup.screenshot({ path: resolve(artifacts, 'popup-cookie-editor.png') }); await popup.getByRole('button', { name: '运行概览' }).click(); const requestUserAgent = async (source) => webPage.evaluate(async (requestSource) => { const response = await fetch(`/api/session?source=${encodeURIComponent(requestSource)}&nonce=${Date.now()}`); return (await response.json()).userAgent; }, source); const waitForRequestUserAgent = async (source, accept) => { let value = ''; for (let attempt = 0; attempt < 30; attempt += 1) { value = await requestUserAgent(`${source}-${attempt}`); if (accept(value)) return value; await webPage.waitForTimeout(100); } return value; }; const browserDefaultUserAgent = await requestUserAgent('ua-default'); await popup.getByRole('button', { name: 'User-Agent', exact: true }).click(); await popup.locator('.popup-ua-view').waitFor(); await popup.getByRole('radio', { name: /Chrome \/ Windows/ }).click(); await popup.getByRole('button', { name: '应用并刷新' }).click(); await popup.locator('.popup-ua-current > strong', { hasText: 'Chrome / Windows' }).waitFor(); const chromeWindowsUserAgent = await waitForRequestUserAgent('ua-chrome-windows', (value) => ( value.includes('Windows NT 10.0') && value.includes('Chrome/138') )); if (!chromeWindowsUserAgent.includes('Windows NT 10.0') || !chromeWindowsUserAgent.includes('Chrome/138')) { throw new Error(`Popup UA preset did not modify real request headers: ${chromeWindowsUserAgent}`); } await popup.getByRole('button', { name: '自定义…' }).click(); const customUAEditor = popup.locator('.popup-ua-custom'); await customUAEditor.locator('input').fill('Popup E2E Agent'); await customUAEditor.locator('textarea').fill('Yakit-Popup-E2E/1.0'); await popup.getByRole('button', { name: '保存、应用并刷新' }).click(); await popup.locator('.popup-ua-current > strong', { hasText: 'Popup E2E Agent' }).waitFor(); const customUserAgent = await waitForRequestUserAgent('ua-custom', (value) => value === 'Yakit-Popup-E2E/1.0'); if (customUserAgent !== 'Yakit-Popup-E2E/1.0') throw new Error(`Popup custom UA did not apply: ${customUserAgent}`); await popup.screenshot({ path: resolve(artifacts, 'popup-user-agent.png') }); await popup.getByRole('radio', { name: /浏览器默认/ }).click(); await popup.getByRole('button', { name: '应用并刷新' }).click(); await popup.locator('.popup-ua-current > strong', { hasText: '浏览器默认' }).waitFor(); const restoredUserAgent = await waitForRequestUserAgent('ua-restored', (value) => value === browserDefaultUserAgent); if (restoredUserAgent !== browserDefaultUserAgent) throw new Error(`Popup UA reset did not restore browser default: ${restoredUserAgent}`); const customProfileCleanup = await popup.evaluate(async () => { const catalog = await chrome.runtime.sendMessage({ action: 'ua.catalog' }); const profile = catalog.data?.find((item) => item.name === 'Popup E2E Agent'); return profile ? await chrome.runtime.sendMessage({ action: 'ua.profile.delete', payload: { id: profile.id } }) : undefined; }); if (customProfileCleanup && !customProfileCleanup.ok) throw new Error(`Could not remove Popup E2E UA profile: ${JSON.stringify(customProfileCleanup)}`); await popup.getByRole('button', { name: '运行概览' }).click(); await webPage.addScriptTag({ path: jsencryptBrowserPath }); await webPage.addScriptTag({ path: sm2BrowserPath }); await webPage.addScriptTag({ path: sm3BrowserPath }); await webPage.addScriptTag({ path: sm4BrowserPath }); await webPage.addScriptTag({ path: nodeForgeBrowserPath }); const cryptoLibraryShape = await webPage.evaluate(() => ({ jsencrypt: typeof window.JSEncrypt, sm2: typeof window.sm2?.doEncrypt, sm3: typeof window.sm3, sm4: typeof window.sm4?.encrypt, forgeCipher: typeof window.forge?.cipher?.createCipher, forgePki: typeof window.forge?.pki?.publicKeyFromPem, })); if (Object.values(cryptoLibraryShape).some((kind) => kind !== 'function')) { throw new Error(`Real browser crypto libraries did not install after the UA reload tests: ${JSON.stringify(cryptoLibraryShape)}`); } await webPage.evaluate(installPageCryptoFixtures, rsaLabPublicKey); await webPage.evaluate(() => { history.pushState({ source: 'e2e-after-ua' }, '', '/authenticated?spa=ua-restored'); history.replaceState({ source: 'e2e' }, '', '/authenticated?spa=inventory'); }); const options = await context.newPage(); await options.setViewportSize({ width: 1440, height: 900 }); await options.goto(`chrome-extension://${extensionId}/options.html?tabId=${targetTab.id}#overview`); await options.locator('.app-shell').waitFor(); await options.getByText('常用工具', { exact: true }).waitFor(); if (await options.locator('.target-tab-select').inputValue() !== String(targetTab.id)) throw new Error('Options did not preserve the explicit target tab'); const lateOpenedPage = await context.newPage(); await lateOpenedPage.goto(testUrl); await lateOpenedPage.evaluate(() => { document.title = 'Opened after Options'; }); const lateOpenedOption = options.locator('.target-tab-select option', { hasText: 'Opened after Options' }); await lateOpenedOption.waitFor({ state: 'attached' }); if (await options.locator('.target-tab-select').inputValue() !== String(targetTab.id)) throw new Error('A newly opened tab replaced the explicit Options target'); await lateOpenedPage.close(); await lateOpenedOption.waitFor({ state: 'detached' }); await options.waitForTimeout(300); await options.screenshot({ path: resolve(artifacts, 'options-overview.png') }); await options.getByRole('button', { name: '引擎连接' }).click(); await options.getByText('连接本机 Yakit', { exact: true }).waitFor(); await options.waitForTimeout(220); await options.screenshot({ path: resolve(artifacts, 'options-engine-unpaired.png') }); const unpairedEngineBounds = await options.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth })); if (unpairedEngineBounds.scrollWidth > unpairedEngineBounds.clientWidth) throw new Error(`Unpaired engine UI overflowed: ${JSON.stringify(unpairedEngineBounds)}`); await options.getByRole('button', { name: '运行概览' }).click(); const strictPage = await context.newPage(); await strictPage.goto(`http://127.0.0.1:${address.port}/strict-csp`); const strictTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))).find((item) => item.url?.includes('/strict-csp')); if (!strictTab?.id) throw new Error('Could not resolve strict CSP tab'); const strictEval = await options.evaluate(async (tabId) => await chrome.runtime.sendMessage({ action: 'context.eval', payload: { tabId, mode: 'expression', code: 'document.title', timeoutMs: 2_000 }, }), strictTab.id); if (shouldEnableUserScripts) { if (!strictEval?.ok || strictEval.data?.value !== 'Strict CSP Page') throw new Error(`Strict CSP page execution failed: ${JSON.stringify(strictEval)}`); } else if (strictEval?.ok) { throw new Error(`Injected fallback unexpectedly bypassed strict page CSP: ${JSON.stringify(strictEval)}`); } await strictPage.close(); const closingPage = await context.newPage(); await closingPage.goto(testUrl); const closingTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))).find((item) => item.url === testUrl && item.id !== targetTab.id); if (!closingTab?.id) throw new Error('Could not resolve closing Eval tab'); const closingEvalPromise = options.evaluate(async (tabId) => await chrome.runtime.sendMessage({ action: 'context.eval', payload: { tabId, mode: 'expression', code: 'new Promise((resolve) => setTimeout(() => resolve(document.title), 2000))', timeoutMs: 5_000 }, }), closingTab.id); await options.waitForTimeout(50); await closingPage.close(); const closingEval = await closingEvalPromise; if (closingEval?.ok || !['target_unavailable', 'request_failed'].includes(closingEval?.errorCode)) throw new Error(`Closing tab Eval did not fail closed: ${JSON.stringify(closingEval)}`); await webPage.evaluate(() => { postMessage({ action: 'grant.create', payload: { scopes: ['browser.page.eval.program'] } }, '*'); dispatchEvent(new CustomEvent('yakit:page-response:v1', { detail: JSON.stringify({ id: 'forged', ok: true, result: { value: 'forged' } }) })); }); const forgedState = await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'state.get' })); if (!forgedState?.ok || forgedState.data?.activeGrant) throw new Error(`Page forged an extension grant: ${JSON.stringify(forgedState)}`); await options.getByRole('button', { name: 'Cookie Editor' }).click(); const testCookie = options.getByRole('button', { name: 'yakit_e2e_session' }); await testCookie.waitFor(); const cookieValue = options.locator('.cookie-value').filter({ hasText: 'authenticated' }).first(); await cookieValue.waitFor(); if (await options.getByTitle(/显示 Cookie 值|隐藏 Cookie 值/).count()) throw new Error('Cookie Editor still renders value visibility controls'); await testCookie.click(); if (await options.locator('.rule-editor input').first().inputValue() !== 'yakit_e2e_session') throw new Error('Cookie Editor did not load the selected HttpOnly cookie'); if (await options.getByText('HttpOnly', { exact: true }).count() === 0) throw new Error('Cookie Editor did not expose HttpOnly metadata'); const cookieTransferChecks = await options.evaluate(async ({ url }) => { const send = async (action, payload) => { const response = await chrome.runtime.sendMessage({ action, payload }); if (!response?.ok) throw new Error(response?.error || action); return response.data; }; const redacted = await send('cookie.export', { url, format: 'set-cookie', includeValues: false }); const sensitive = await send('cookie.export', { url, format: 'json', includeValues: true }); const imports = []; imports.push(await send('cookie.import', { url, format: 'json', text: JSON.stringify([{ name: 'json_import', value: 'json-value', path: '/' }]) })); imports.push(await send('cookie.import', { url, format: 'netscape', text: '127.0.0.1\tFALSE\t/\tFALSE\t0\tnetscape_import\tnetscape-value\n' })); imports.push(await send('cookie.import', { url, format: 'set-cookie', text: 'Set-Cookie: raw_import=raw-value; Path=/; HttpOnly; SameSite=Lax; Priority=High' })); const listed = await send('cookie.list', { url }); const imported = listed.filter((cookie) => ['json_import', 'netscape_import', 'raw_import'].includes(cookie.name)); const removed = await send('cookie.removeMany', { cookies: imported.map((cookie) => ({ url: `${cookie.secure ? 'https' : 'http'}://${cookie.domain.replace(/^\./, '')}${cookie.path}`, name: cookie.name, storeId: cookie.storeId, partitionKey: cookie.partitionKey, })) }); return { redacted, sensitive, imports, imported: imported.map((cookie) => cookie.name), removed }; }, { url: testUrl }); if (!cookieTransferChecks.redacted.includes('[REDACTED]') || cookieTransferChecks.redacted.includes('authenticated')) throw new Error(`Cookie export was not redacted: ${cookieTransferChecks.redacted}`); if (!cookieTransferChecks.sensitive.includes('authenticated')) throw new Error('Explicit Cookie value export omitted values'); if (cookieTransferChecks.imported.length !== 3 || cookieTransferChecks.removed.removed !== 3 || cookieTransferChecks.imports[2].warnings.length === 0) { throw new Error(`Cookie import/export/bulk delete failed: ${JSON.stringify(cookieTransferChecks)}`); } await options.screenshot({ path: resolve(artifacts, 'options-cookie-editor.png') }); await options.getByRole('button', { name: 'UA 快速切换' }).click(); await options.getByRole('heading', { name: 'User-Agent 快速切换' }).waitFor(); await options.getByText('每个 hostname 只保留一个生效预设', { exact: true }).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-user-agent.png') }); await options.getByRole('button', { name: '登录态工作区' }).click(); await options.getByRole('tab', { name: '主世界 Eval' }).click(); await options.screenshot({ path: resolve(artifacts, 'options-context-eval.png') }); const protocolChecks = await options.evaluate(async () => { const invalid = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: true, unexpected: true } }); const proxyResponse = await chrome.runtime.sendMessage({ action: 'proxy.switch', payload: { id: 'direct' } }); const proxySettings = await chrome.proxy.settings.get({}); await Promise.all([ chrome.runtime.sendMessage({ action: 'panel.update', payload: { side: 'right' } }), chrome.runtime.sendMessage({ action: 'panel.update', payload: { y: 0.46 } }), ]); const state = await chrome.runtime.sendMessage({ action: 'state.get' }); return { invalid, proxyResponse, proxyMode: proxySettings.value?.mode, floatingPanel: state.data?.floatingPanel }; }); if (protocolChecks.invalid?.ok || !protocolChecks.invalid?.error?.includes('参数无效')) throw new Error(`Runtime schema accepted an unknown field: ${JSON.stringify(protocolChecks.invalid)}`); if (protocolChecks.floatingPanel?.side !== 'right' || protocolChecks.floatingPanel?.y !== 0.46) throw new Error(`Concurrent state updates lost data: ${JSON.stringify(protocolChecks.floatingPanel)}`); if (!protocolChecks.proxyResponse?.ok || protocolChecks.proxyMode !== 'direct') throw new Error(`Direct proxy mode was not applied explicitly: ${JSON.stringify(protocolChecks)}`); const proxyRuleChecks = await options.evaluate(async ({ url, sourceUrl }) => { const send = async (action, payload) => { const response = await chrome.runtime.sendMessage({ action, payload }); if (!response?.ok) throw new Error(response?.error || action); return response.data; }; const now = Date.now(); const directRule = { id: 'e2e-direct-rule', name: 'E2E direct', enabled: true, condition: { type: 'host_exact', value: '127.0.0.1' }, proxyProfileId: 'direct', order: 0, createdAt: now, updatedAt: now }; const mitmRule = { id: 'e2e-mitm-rule', name: 'E2E MITM', enabled: true, condition: { type: 'host_exact', value: '127.0.0.1' }, proxyProfileId: 'yakit-mitm', order: 1, createdAt: now, updatedAt: now }; await send('proxy.rule.save', directRule); await send('proxy.rule.save', mitmRule); await send('proxy.rules.settings', { defaultProfileId: 'direct', failMode: 'open' }); const firstPreview = await send('proxy.rules.preview', { url }); const pac = await send('proxy.rules.compile'); const auth = await send('proxy.auth.set', { profileId: 'yakit-mitm', password: 'proxy-session-secret-818' }); const authStatus = await send('proxy.auth.status', { profileId: 'yakit-mitm' }); const reordered = await send('proxy.rules.reorder', { ids: ['e2e-mitm-rule', 'e2e-direct-rule'] }); const secondPreview = await send('proxy.rules.preview', { url }); await send('proxy.rules.reorder', { ids: ['e2e-direct-rule', 'e2e-mitm-rule'] }); const savedSource = await send('proxy.source.save', { name: 'E2E AutoProxy subscription', url: sourceUrl, format: 'auto', enabled: true, matchProfileId: 'yakit-mitm', bypassProfileId: 'direct', updateIntervalMinutes: 60, }); const refreshedSourceState = await send('proxy.source.refresh', { id: savedSource.id }); const sourcePage = await send('proxy.source.rules', { id: savedSource.id, offset: 0, limit: 100 }); const blockedPreview = await send('proxy.rules.preview', { url: 'https://blocked.subscription.test/path' }); const allowedPreview = await send('proxy.rules.preview', { url: 'https://allowed.subscription.test/path' }); const configuration = await send('proxy.config.export'); const imported = await send('proxy.config.import', { configuration }); const applied = await send('proxy.auto.apply'); return { firstPreview, secondPreview, blockedPreview, allowedPreview, pac, auth, authStatus, applied, sourcePage, refreshedSources: refreshedSourceState.proxyRuleSources, reordered: reordered.proxyRules, imported: imported.proxyRouting, configuration }; }, { url: testUrl, sourceUrl: new URL('/proxy-rules', testUrl).toString() }); if (proxyRuleChecks.firstPreview.matchedRuleId !== 'e2e-direct-rule' || proxyRuleChecks.firstPreview.effectiveProfileId !== 'direct' || proxyRuleChecks.secondPreview.effectiveProfileId !== 'yakit-mitm') { throw new Error(`Proxy deterministic order/preview failed: ${JSON.stringify(proxyRuleChecks)}`); } if (!proxyRuleChecks.pac.pacScript.includes('PROXY 127.0.0.1:8083; DIRECT') || !proxyRuleChecks.auth.configured || !proxyRuleChecks.authStatus.configured || proxyRuleChecks.applied.activeProxyId !== 'auto') { throw new Error(`Proxy PAC/fail-open/auth failed: ${JSON.stringify(proxyRuleChecks)}`); } if (proxyRuleChecks.sourcePage.total !== 2 || proxyRuleChecks.blockedPreview.effectiveProfileId !== 'yakit-mitm' || proxyRuleChecks.allowedPreview.effectiveProfileId !== 'direct' || !proxyRuleChecks.configuration.sources[0]?.content?.includes('[AutoProxy 0.2]')) { throw new Error(`Proxy subscription/IndexedDB/config exchange failed: ${JSON.stringify(proxyRuleChecks)}`); } await options.getByRole('button', { name: '自动切换' }).click(); await options.getByRole('heading', { name: '自动切换' }).waitFor(); await options.getByText('配置与浏览器一致', { exact: true }).waitFor(); await options.waitForTimeout(350); await options.screenshot({ path: resolve(artifacts, 'options-auto-switch.png') }); await options.getByRole('button', { name: '规则订阅' }).click(); await options.getByRole('heading', { name: '规则订阅' }).waitFor(); await options.getByRole('heading', { name: 'E2E AutoProxy subscription' }).waitFor(); await options.getByText('规范化规则', { exact: true }).waitFor(); await options.waitForTimeout(350); await options.screenshot({ path: resolve(artifacts, 'options-rule-sources.png') }); await options.evaluate(async () => { await chrome.runtime.sendMessage({ action: 'proxy.auth.set', payload: { profileId: 'yakit-mitm', password: '' } }); await chrome.runtime.sendMessage({ action: 'proxy.switch', payload: { id: 'direct' } }); }); const host = webPage.locator('yakit-browser-agent'); await host.waitFor({ state: 'attached', timeout: 10_000 }); const launcher = webPage.locator('.floating-panel__brand'); await launcher.waitFor({ state: 'visible', timeout: 10_000 }); const launcherImageLoaded = await launcher.locator('img').evaluate((image) => image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0); if (!launcherImageLoaded) throw new Error('Floating Yak asset did not load'); await launcher.click(); await webPage.locator('.floating-panel.is-expanded').waitFor(); await webPage.waitForTimeout(250); const extensionTabs = await serviceWorker.evaluate(async () => await chrome.tabs.query({})); const optionsTab = extensionTabs.find((tab) => tab.url?.includes('/options.html')); const floatingFrame = webPage.frames().find((frame) => frame.url().includes('/floating.html')); if (!optionsTab?.id || !floatingFrame) throw new Error('Could not resolve floating frame sender boundary'); await floatingFrame.locator('.floating-panel--embedded .floating-panel__body').waitFor({ state: 'visible', timeout: 10_000 }); await floatingFrame.getByText('快速切换', { exact: true }).waitFor({ state: 'visible' }); const floatingBrandLoaded = await floatingFrame.locator('.floating-panel__brand img').evaluate((image) => ( image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0 )); if (!floatingBrandLoaded) throw new Error('Expanded floating panel Yak asset did not load'); await floatingFrame.evaluate(async () => { await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame))); }); await webPage.evaluate(async () => { await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame))); }); const crossTabGrant = await floatingFrame.evaluate(async (tabId) => await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }], scopes: ['browser.tabs.read', 'browser.dom.read'], durationMinutes: 5, }, }), optionsTab.id); if (crossTabGrant?.ok || !crossTabGrant?.error?.includes('当前标签页')) throw new Error(`Floating frame crossed its sender tab boundary: ${JSON.stringify(crossTabGrant)}`); const rightMetrics = await webPage.locator('.floating-panel').evaluate((panel) => { const rect = panel.getBoundingClientRect(); return { width: rect.width, left: rect.left, right: rect.right, viewportWidth: innerWidth }; }); if (rightMetrics.width !== 326 || rightMetrics.left < 0 || rightMetrics.right > rightMetrics.viewportWidth) { throw new Error(`Right floating panel is clipped: ${JSON.stringify(rightMetrics)}`); } await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-right.png') }); const headerBox = await webPage.locator('.floating-panel__header').boundingBox(); if (!headerBox) throw new Error('Floating panel header has no layout box'); await webPage.mouse.move(headerBox.x + 20, headerBox.y + 20); await webPage.mouse.down(); await webPage.mouse.move(22, 300, { steps: 8 }); await webPage.mouse.up(); await webPage.waitForTimeout(250); const snappedLeft = await webPage.locator('.floating-panel').evaluate((panel) => panel.classList.contains('floating-panel--left')); if (!snappedLeft) throw new Error('Floating panel did not snap to the left edge'); await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-left.png') }); await options.evaluate(async ({ endpoint, tabId }) => { const send = async (action, payload) => { const response = await chrome.runtime.sendMessage({ action, payload }); if (!response?.ok) throw new Error(response?.error || action); return response.data; }; const state = await send('state.get'); await send('bridge.config.save', { transport: 'websocket', nativeHost: 'com.yaklang.browser_agent', endpoint, autoConnect: false, installationId: state.bridge.installationId, }); await send('bridge.pair'); for (let attempt = 0; attempt < 50; attempt += 1) { const [nextState, bridgeStatus] = await Promise.all([send('state.get'), send('bridge.status')]); if (nextState.bridge.pairedEngine && bridgeStatus.state === 'connected') break; await new Promise((resolve) => setTimeout(resolve, 50)); if (attempt === 49) throw new Error(`Bridge v3 did not connect: ${JSON.stringify({ nextState, bridgeStatus })}`); } await send('grant.create', { targets: [{ tabId, frameId: 0 }], scopes: ['browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read'], durationMinutes: 5, }); }, { endpoint: bridgeEndpoint, tabId: targetTab.id }); const { socket: bridgeSocket, hello } = await bridgeConnection; if (hello.type !== 'auth' || hello.client !== 'yakit-browser-extension' || hello.protocolVersion !== 3 || !hello.installationId || !hello.signature || !hello.capabilities?.includes('browser.eval')) { throw new Error(`Unexpected Bridge hello: ${JSON.stringify(hello)}`); } await options.getByRole('button', { name: '引擎连接' }).click(); await options.getByText('浏览器已安全配对', { exact: true }).waitFor(); await options.waitForTimeout(220); await options.screenshot({ path: resolve(artifacts, 'options-engine-paired.png') }); await options.setViewportSize({ width: 390, height: 844 }); await options.waitForTimeout(150); const narrowEngineBounds = await options.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth })); if (narrowEngineBounds.scrollWidth > narrowEngineBounds.clientWidth) throw new Error(`Narrow engine UI overflowed: ${JSON.stringify(narrowEngineBounds)}`); await options.screenshot({ path: resolve(artifacts, 'options-engine-paired-narrow.png') }); await options.setViewportSize({ width: 1440, height: 900 }); await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'agent.pause' }); if (!response?.ok || response.data?.state !== 'paused') throw new Error(response?.error || 'agent.pause'); }); const pausedCall = await callBridge(bridgeSocket, 'verify-agent-paused', 'browser.tabs', {}); if (pausedCall.error?.code !== 'agent_paused') throw new Error(`Paused Agent accepted a capability: ${JSON.stringify(pausedCall)}`); await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'agent.resume' }); if (!response?.ok || response.data?.state !== 'running') throw new Error(response?.error || 'agent.resume'); }); const deniedBridgeEval = await callBridge(bridgeSocket, 'verify-read-denied', 'browser.eval', { tabId: targetTab.id, mode: 'expression', code: 'document.title', }); if (!deniedBridgeEval.error?.message?.includes('browser.page.eval')) { throw new Error(`Read grant unexpectedly allowed browser.eval: ${JSON.stringify(deniedBridgeEval)}`); } await options.evaluate(async ({ tabId }) => { const response = await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }], scopes: [ 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', 'browser.recording.read', 'browser.recording.control', 'browser.proxy.read', 'browser.proxy.write', ], durationMinutes: 5, }, }); if (!response?.ok) throw new Error(response?.error || 'grant.create'); }, { tabId: targetTab.id }); const frameInventory = await callBridge(bridgeSocket, 'verify-frame-inventory', 'browser.frames', { tabId: targetTab.id }); const sameOriginFrame = frameInventory.result?.find((frame) => frame.url?.includes('/frame-same')); const crossOriginFrame = frameInventory.result?.find((frame) => frame.url?.includes('/frame-cross')); if (frameInventory.error || !sameOriginFrame?.accessible || !sameOriginFrame.sameOrigin || !crossOriginFrame?.accessible || crossOriginFrame.sameOrigin) { throw new Error(`Frame inventory did not distinguish same/cross-origin frames: ${JSON.stringify(frameInventory)}`); } const deniedCrossFrame = await callBridge(bridgeSocket, 'verify-cross-frame-denied', 'browser.context', { tabId: targetTab.id, frameId: crossOriginFrame.frameId, }); if (deniedCrossFrame.error?.code !== 'target_denied') { throw new Error(`Unselected cross-origin frame was readable: ${JSON.stringify(deniedCrossFrame)}`); } await options.evaluate(async ({ tabId, frameIds }) => { const response = await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }, ...frameIds.map((frameId) => ({ tabId, frameId }))], scopes: [ 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', 'browser.recording.read', 'browser.recording.control', 'browser.proxy.read', 'browser.proxy.write', ], durationMinutes: 5, }, }); if (!response?.ok) throw new Error(response?.error || 'grant.create frames'); }, { tabId: targetTab.id, frameIds: [sameOriginFrame.frameId, crossOriginFrame.frameId] }); const crossFrameContext = await callBridge(bridgeSocket, 'verify-cross-frame-context', 'browser.context', { tabId: targetTab.id, frameId: crossOriginFrame.frameId, includeDom: true, }); if (crossFrameContext.error || crossFrameContext.result?.document?.title !== 'Cross Origin Account Frame') { throw new Error(`Explicitly granted cross-origin frame was not readable: ${JSON.stringify(crossFrameContext)}`); } const sharedTabs = await callBridge(bridgeSocket, 'verify-deduplicated-tabs', 'browser.tabs', {}); if (sharedTabs.error || sharedTabs.result?.length !== 1 || sharedTabs.result[0]?.id !== targetTab.id) { throw new Error(`browser.tabs did not de-duplicate multi-frame grants: ${JSON.stringify(sharedTabs)}`); } const deniedProgramEval = await callBridge(bridgeSocket, 'verify-program-eval-denied', 'browser.eval', { tabId: targetTab.id, mode: 'program', code: 'const value = 41; value + 1', }); if (!deniedProgramEval.error?.message?.includes('browser.page.eval.program')) { throw new Error(`Expression-only grant allowed program Eval: ${JSON.stringify(deniedProgramEval)}`); } const recordingStart = await callBridge(bridgeSocket, 'verify-recording-start', 'browser.recording.start', { tabId: targetTab.id, captureValues: false, maxEntries: 200, }); if (recordingStart.error || recordingStart.result?.status?.active !== true) { throw new Error(`Browser recording did not start: ${JSON.stringify(recordingStart)}`); } await webPage.evaluate(async (socketPort) => { const form = document.querySelector('form'); form.addEventListener('submit', (event) => event.preventDefault(), { once: true }); form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); const linkedValue = window.CryptoJS.SHA256('recorder-linked-secret-171').toString(); await fetch('/api/session?source=recorder-linked-fetch', { method: 'POST', body: linkedValue }); const formLinkedValue = window.CryptoJS.SHA256('recorder-form-linked-secret-181').toString(); await fetch('/api/session?source=recorder-form-linked-fetch', { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: new URLSearchParams({ encryptedData: formLinkedValue, channel: 'browser' }), }); const rsaPlaintext = JSON.stringify({ username: 'recorder-rsa-admin-191', password: 'recorder-rsa-password-192' }); const rsaCiphertext = window.__yakitRsa.encrypt(rsaPlaintext); const rsaResponse = await fetch('/encrypt/rsa.php?source=recorder-rsa-profile', { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: new URLSearchParams({ data: rsaCiphertext }), }); const rsaValidation = await rsaResponse.json(); if (!rsaValidation.ok || rsaValidation.plaintext !== rsaPlaintext) { throw new Error(`Independent RSA server rejected the browser ciphertext: ${JSON.stringify(rsaValidation)}`); } await new Promise((resolveRequest, rejectRequest) => { const request = new XMLHttpRequest(); request.open('POST', '/api/session?source=recorder-xhr'); request.onload = resolveRequest; request.onerror = rejectRequest; request.send('recorder-xhr-secret-272'); }); await crypto.subtle.digest('SHA-256', new TextEncoder().encode('recorder-webcrypto-secret-373')); await new Promise((resolveSocket, rejectSocket) => { const socket = new WebSocket(`ws://127.0.0.1:${socketPort}/page-socket`); socket.onopen = () => socket.send('recorder-websocket-secret-575'); socket.onmessage = () => socket.close(); socket.onclose = resolveSocket; socket.onerror = rejectSocket; }); const marker = document.createElement('button'); marker.textContent = 'Worker boundary holdout'; document.body.append(marker); marker.click(); marker.remove(); await new Promise((resolveWorker, rejectWorker) => { const worker = new Worker('/opaque-worker.js?chunk=randomized-811'); worker.addEventListener('message', async (event) => { try { const response = await fetch('/opaque-worker-submit?route=randomized-812', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(event.data), }); const result = await response.json(); worker.terminate(); if (!result.ok) throw new Error(JSON.stringify(result)); resolveWorker(); } catch (error) { rejectWorker(error); } }, { once: true }); worker.addEventListener('error', rejectWorker, { once: true }); worker.postMessage({ marker: 'worker-boundary-holdout-811', nested: { value: 812 } }); }); await new Promise((resolveMessage) => { const channel = new MessageChannel(); channel.port1.onmessage = () => { channel.port1.close(); channel.port2.close(); resolveMessage(); }; channel.port2.postMessage({ marker: 'message-port-boundary-813' }); }); if (!navigator.sendBeacon('/beacon-boundary?route=randomized-814', 'beacon-boundary-value-814')) { throw new Error('sendBeacon rejected the E2E boundary payload'); } }, address.port); await webPage.evaluate(async ({ smPublicKey, smPrivateKey, sm4Key, sm4IV, forgePublicKey, forgeAesKey, forgeAesIV, }) => { const marker = document.createElement('button'); marker.textContent = 'Semantic adapter holdout'; document.body.append(marker); marker.click(); marker.remove(); const expectedSMPlaintext = 'semantic-sm-plaintext-821'; const sm2Ciphertext = window.sm2.doEncrypt(expectedSMPlaintext, smPublicKey, 1); const sm2Signature = window.sm2.doSignature(expectedSMPlaintext, smPrivateKey); const sm3Digest = window.sm3(expectedSMPlaintext); const sm4Ciphertext = window.sm4.encrypt(expectedSMPlaintext, sm4Key, { mode: 'cbc', iv: sm4IV }); if (window.sm2.doDecrypt(sm2Ciphertext, smPrivateKey, 1) !== expectedSMPlaintext || !window.sm2.doVerifySignature(expectedSMPlaintext, sm2Signature, smPublicKey) || window.sm4.decrypt(sm4Ciphertext, sm4Key, { mode: 'cbc', iv: sm4IV }) !== expectedSMPlaintext) { throw new Error('Browser sm-crypto self verification failed'); } const expectedForgePlaintext = 'semantic-forge-plaintext-822'; const cipher = window.forge.cipher.createCipher('AES-CBC', window.forge.util.hexToBytes(forgeAesKey)); cipher.start({ iv: window.forge.util.hexToBytes(forgeAesIV) }); cipher.update(window.forge.util.createBuffer(expectedForgePlaintext, 'utf8')); if (!cipher.finish()) throw new Error('Browser node-forge AES encryption failed'); const forgeAesCiphertext = cipher.output.toHex(); const publicKey = window.forge.pki.publicKeyFromPem(forgePublicKey); const forgeRsaCiphertext = window.forge.util.encode64(publicKey.encrypt( expectedForgePlaintext, 'RSAES-PKCS1-V1_5', )); const digest = window.forge.md.sha256.create(); digest.update(expectedForgePlaintext, 'utf8'); const forgeDigest = digest.digest().toHex(); const hmac = window.forge.hmac.create(); hmac.start('sha256', window.forge.util.hexToBytes(forgeAesKey)); hmac.update(expectedForgePlaintext); const forgeHmac = hmac.digest().toHex(); const response = await fetch('/semantic-adapter-submit?route=randomized-823', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ expectedSMPlaintext, sm2Ciphertext, sm2Signature, sm3Digest, sm4Ciphertext, expectedForgePlaintext, forgeAesCiphertext, forgeRsaCiphertext, forgeDigest, forgeHmac, }), }); const result = await response.json(); if (!result.ok) throw new Error(`Independent semantic adapter server rejected the envelope: ${JSON.stringify(result)}`); }, { smPublicKey: smLabKeyPair.publicKey, smPrivateKey: smLabKeyPair.privateKey, sm4Key: smLabSM4Key, sm4IV: smLabSM4IV, forgePublicKey: rsaLabPublicKey, forgeAesKey: forgeLabAESKey, forgeAesIV: forgeLabAESIV, }); await webPage.locator('#opaque-module-lab[data-ready="true"]').waitFor(); await webPage.locator('#opaque-module-input').fill(closureInitialMarker); await webPage.locator('#opaque-module-submit').click(); await webPage.locator('#opaque-module-result').getByText(closureInitialMarker, { exact: true }).waitFor(); await webPage.locator('#crypto-lab[data-ready="true"]').waitFor(); await webPage.locator('#crypto-password').fill('recorder-webcrypto-envelope-474'); await webPage.locator('#crypto-submit').click(); await webPage.locator('#crypto-result').getByText('recorder-webcrypto-envelope-474').waitFor(); const recordingSnapshot = await callBridge(bridgeSocket, 'verify-recording-get', 'browser.recording.get', { tabId: targetTab.id, limit: 200 }); const observedKinds = new Set(recordingSnapshot.result?.events?.map((item) => item.kind)); for (const kind of ['fetch', 'xhr', 'form', 'beacon', 'worker', 'message', 'websocket', 'crypto']) { if (!observedKinds.has(kind)) throw new Error(`Browser recording missed ${kind}: ${JSON.stringify(recordingSnapshot)}`); } const workerSendEvent = recordingSnapshot.result?.events?.find((item) => item.kind === 'worker' && item.operation === 'worker.postMessage'); const workerReceiveEvent = recordingSnapshot.result?.events?.find((item) => item.kind === 'worker' && item.operation === 'worker.message'); const workerRequestEvent = recordingSnapshot.result?.events?.find((item) => item.kind === 'fetch' && item.url?.includes('/opaque-worker-submit')); if (!workerSendEvent?.wrapperHandleId || !workerReceiveEvent || workerSendEvent.channelId !== workerReceiveEvent.channelId || workerSendEvent.traceId !== workerReceiveEvent.traceId || !recordingSnapshot.result?.links?.some((item) => item.kind === 'channel' && item.fromEventId === workerSendEvent.id && item.toEventId === workerReceiveEvent.id)) { throw new Error(`Worker boundary did not retain its exact handle and async Trace correlation: ${JSON.stringify(recordingSnapshot)}`); } const unknownBoundaryCandidate = recordingSnapshot.result?.profileCandidates?.find((candidate) => ( candidate.request.eventId === workerRequestEvent?.id && candidate.source.operation === 'unknown-business-envelope' )); if (!unknownBoundaryCandidate || unknownBoundaryCandidate.status !== 'capture-required') { throw new Error(`Unknown Worker/ESM boundary did not remain actionable: ${JSON.stringify(recordingSnapshot)}`); } const observedCryptoProviders = new Set(recordingSnapshot.result?.events ?.filter((item) => item.kind === 'crypto') .map((item) => item.crypto?.adapterId)); for (const provider of ['webcrypto', 'cryptojs', 'jsencrypt', 'sm-crypto', 'node-forge']) { if (!observedCryptoProviders.has(provider)) { throw new Error(`Browser recording missed the ${provider} crypto adapter: ${JSON.stringify(recordingSnapshot)}`); } } const smOperations = new Set(recordingSnapshot.result?.events ?.filter((item) => item.kind === 'crypto' && item.crypto?.adapterId === 'sm-crypto') .map((item) => item.crypto?.operation)); for (const operation of ['sm2.encrypt', 'sm2.decrypt', 'sm2.sign', 'sm2.verify', 'sm3.digest', 'sm4.encrypt', 'sm4.decrypt']) { if (!smOperations.has(operation)) throw new Error(`Real sm-crypto fixture missed ${operation}: ${JSON.stringify(recordingSnapshot)}`); } const forgeEvents = recordingSnapshot.result?.events ?.filter((item) => item.kind === 'crypto' && item.crypto?.adapterId === 'node-forge') || []; for (const phase of ['create', 'init', 'update', 'final']) { if (!forgeEvents.some((item) => item.crypto?.state?.phase === phase && item.crypto?.state?.correlationId)) { throw new Error(`Real node-forge fixture missed correlated ${phase} state: ${JSON.stringify(recordingSnapshot)}`); } } if (!forgeEvents.some((item) => item.crypto?.operation === 'rsa.encrypt' && item.callHandleId && item.callableCapable) || !forgeEvents.some((item) => item.crypto?.operation === 'cipher.encrypt.output.toHex')) { throw new Error(`Real node-forge fixture missed RSA callable or cipher output boundary: ${JSON.stringify(recordingSnapshot)}`); } const linkedCryptoEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'cryptojs' && item.crypto?.operation === 'SHA256' )); const webCryptoEncryptEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'webcrypto' && item.crypto?.operation === 'encrypt' && item.wrapperHandleId )); const webCryptoDecryptEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'webcrypto' && item.crypto?.operation === 'decrypt' && item.wrapperHandleId )); const closureRequestEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'fetch' && item.url?.includes(closureSubmitPath) )); const webCryptoDigestEvents = recordingSnapshot.result?.events?.filter((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'webcrypto' && item.crypto?.operation === 'digest' && item.wrapperHandleId )) || []; const webCryptoDigestEvent = webCryptoDigestEvents.find((item) => item.scriptUrl?.includes(closureModulePath)) || webCryptoDigestEvents[0]; const exactValueLinks = recordingSnapshot.result?.links?.filter((item) => ( item.kind === 'value' && item.confidence === 'exact' )) || []; const closureReachableEvents = new Set(webCryptoDigestEvent ? [webCryptoDigestEvent.id] : []); for (let pass = 0; pass < exactValueLinks.length; pass += 1) { let changed = false; for (const link of exactValueLinks) { if (closureReachableEvents.has(link.fromEventId) && !closureReachableEvents.has(link.toEventId)) { closureReachableEvents.add(link.toEventId); changed = true; } } if (!changed) break; } const closureDigestLink = recordingSnapshot.result?.links?.find((item) => ( closureReachableEvents.has(item.fromEventId) && item.toEventId === closureRequestEvent?.id && item.toPath === `$body:json.${closureDigestField}` && item.kind === 'value' && item.confidence === 'exact' )); if (!linkedCryptoEvent?.wrapperHandleId || !webCryptoEncryptEvent?.wrapperHandleId || !webCryptoDecryptEvent?.wrapperHandleId || !webCryptoDigestEvent?.wrapperHandleId) { throw new Error(`Recording did not retain exact crypto wrapper handles: ${JSON.stringify({ linkedCryptoEvent, webCryptoEncryptEvent, webCryptoDecryptEvent, webCryptoDigestEvents, cryptoOperations: recordingSnapshot.result?.events?.filter((item) => item.kind === 'crypto') .map((item) => `${item.crypto?.adapterId}:${item.crypto?.operation}:${Boolean(item.wrapperHandleId)}`), })}`); } if (!closureRequestEvent || !closureDigestLink) { throw new Error(`Randomized ESM/WASM holdout did not use the generic evidence graph: ${JSON.stringify({ closureSubmitPath, closureDigestField, request: closureRequestEvent, digests: webCryptoDigestEvents, links: recordingSnapshot.result?.links?.filter((item) => item.toEventId === closureRequestEvent?.id), })}`); } const linkedFetchEvent = recordingSnapshot.result?.events?.find((item) => item.kind === 'fetch' && item.url?.includes('recorder-linked-fetch')); if (!linkedCryptoEvent || !linkedFetchEvent || !recordingSnapshot.result?.links?.some((link) => link.fromEventId === linkedCryptoEvent.id && link.toEventId === linkedFetchEvent.id)) { throw new Error(`Recording did not link CryptoJS output to Fetch input: ${JSON.stringify(recordingSnapshot)}`); } if (!recordingSnapshot.result?.traces?.some((trace) => trace.eventIds.includes(linkedCryptoEvent.id) && trace.eventIds.includes(linkedFetchEvent.id))) { throw new Error(`Linked events were not grouped into one business Trace: ${JSON.stringify(recordingSnapshot)}`); } const inferredProfile = recordingSnapshot.result?.profileCandidates?.find((candidate) => ( candidate.source?.eventId === linkedCryptoEvent.id && candidate.request?.eventId === linkedFetchEvent.id )); if (inferredProfile?.status !== 'ready' || inferredProfile.request?.destination !== 'body' || inferredProfile.request?.serialization !== 'raw-body' || inferredProfile.confidence?.level !== 'high' || inferredProfile.source?.arguments?.[0]?.role !== 'data' || inferredProfile.aiContext?.valuePolicy !== 'metadata-only') { throw new Error(`Recording did not infer a safe high-confidence Profile candidate: ${JSON.stringify(recordingSnapshot)}`); } const formLinkedFetchEvent = recordingSnapshot.result?.events?.find((item) => item.kind === 'fetch' && item.url?.includes('recorder-form-linked-fetch')); const formFieldLink = recordingSnapshot.result?.links?.find((link) => ( link.toEventId === formLinkedFetchEvent?.id && link.toPath === '$body:form.encryptedData' )); const formLinkedCandidate = recordingSnapshot.result?.profileCandidates?.find((candidate) => ( candidate.source?.eventId === formFieldLink?.fromEventId && candidate.request?.eventId === formLinkedFetchEvent?.id )); if (!formLinkedFetchEvent || !formFieldLink || formLinkedCandidate?.request?.destination !== 'body.encryptedData' || formLinkedCandidate?.request?.serialization !== 'form-field' || formLinkedCandidate?.status !== 'ready' || formLinkedCandidate?.confidence?.level !== 'high') { throw new Error(`Recording did not preserve the generic form field value chain: ${JSON.stringify(recordingSnapshot)}`); } const rsaCryptoEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'jsencrypt' && item.crypto?.operation === 'encrypt' )); const rsaRequestEvent = recordingSnapshot.result?.events?.find((item) => ( item.kind === 'fetch' && item.url?.includes('/encrypt/rsa.php?source=recorder-rsa-profile') )); const rsaFieldLink = recordingSnapshot.result?.links?.find((link) => ( link.fromEventId === rsaCryptoEvent?.id && link.toEventId === rsaRequestEvent?.id && link.toPath === '$body:form.data' )); const rsaCandidate = recordingSnapshot.result?.profileCandidates?.find((candidate) => ( candidate.source?.eventId === rsaCryptoEvent?.id && candidate.request?.eventId === rsaRequestEvent?.id )); if (!rsaCryptoEvent || !rsaRequestEvent || !rsaFieldLink || rsaCryptoEvent.crypto?.family !== 'asymmetric' || rsaCryptoEvent.crypto?.padding !== 'PKCS1-v1_5' || rsaCryptoEvent.crypto?.key?.kind !== 'public' || rsaCryptoEvent.crypto?.key?.bits !== 1024 || !rsaCryptoEvent.crypto?.key?.fingerprint || rsaCandidate?.status !== 'ready' || rsaCandidate.request?.destination !== 'body.data' || rsaCandidate.request?.serialization !== 'form-field') { throw new Error(`JSEncrypt RSA was not inferred as an executable form-field Profile: ${JSON.stringify(recordingSnapshot)}`); } const rsaAIContext = JSON.stringify(rsaCandidate.aiContext); if (rsaAIContext.includes('BEGIN PUBLIC KEY') || rsaAIContext.includes(rsaLabPublicModulusHex)) { throw new Error(`JSEncrypt inference leaked raw public-key material: ${rsaAIContext}`); } const redactedRecording = JSON.stringify(recordingSnapshot.result); for (const secret of ['recorder-linked-secret-171', 'recorder-form-linked-secret-181', 'recorder-rsa-admin-191', 'recorder-rsa-password-192', 'recorder-xhr-secret-272', 'recorder-webcrypto-secret-373', 'recorder-webcrypto-envelope-474', 'recorder-websocket-secret-575', 'worker-boundary-holdout-811', 'message-port-boundary-813', 'beacon-boundary-value-814', closureInitialMarker, 'BEGIN PUBLIC KEY']) { if (redactedRecording.includes(secret)) throw new Error(`Metadata-only recording leaked a value: ${secret}`); } for (const keyMaterial of [smLabKeyPair.privateKey, smLabSM4Key, forgeLabAESKey, 'semantic-sm-plaintext-821', 'semantic-forge-plaintext-822', 'BEGIN PRIVATE KEY']) { if (redactedRecording.includes(keyMaterial)) throw new Error('Metadata-only recording leaked semantic adapter key material or plaintext'); } const deniedSensitiveRecording = await callBridge(bridgeSocket, 'verify-recording-sensitive-denied', 'browser.recording.start', { tabId: targetTab.id, captureValues: true, }); if (!deniedSensitiveRecording.error?.message?.includes('browser.recording.sensitive.read')) { throw new Error(`Recording value capture did not require its sensitive scope: ${JSON.stringify(deniedSensitiveRecording)}`); } await callBridge(bridgeSocket, 'verify-recording-stop-metadata', 'browser.recording.stop', { tabId: targetTab.id }); await options.evaluate(async ({ tabId, frameIds }) => { const response = await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }, ...frameIds.map((frameId) => ({ tabId, frameId }))], scopes: [ 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.page.eval.program', 'browser.human.takeover', 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', 'browser.recording.read', 'browser.recording.control', 'browser.recording.sensitive.read', 'browser.callable.execute', 'browser.debugger.read', 'browser.debugger.control', 'browser.transform.read', 'browser.transform.manage', 'browser.transform.execute', 'browser.proxy.read', 'browser.proxy.write', ], durationMinutes: 5, }, }); if (!response?.ok) throw new Error(response?.error || 'grant.create recording sensitive'); }, { tabId: targetTab.id, frameIds: [sameOriginFrame.frameId, crossOriginFrame.frameId] }); const sensitiveRecordingStart = await callBridge(bridgeSocket, 'verify-recording-sensitive-start', 'browser.recording.start', { tabId: targetTab.id, captureValues: true, }); if (sensitiveRecordingStart.error) throw new Error(`Sensitive browser recording did not start: ${JSON.stringify(sensitiveRecordingStart)}`); const programEval = await callBridge(bridgeSocket, 'verify-program-eval', 'browser.eval', { tabId: targetTab.id, mode: 'program', code: 'const programValue = 41; return programValue + 1', }); if (programEval.error || programEval.result?.value !== 42) throw new Error(`Program Eval scope did not execute: ${JSON.stringify(programEval)}`); await webPage.evaluate(() => { window.CryptoJS.SHA256('recorder-sensitive-preview-686'); window.__yakitRsa.encrypt(JSON.stringify({ username: 'sensitive-rsa-admin-687', password: 'sensitive-rsa-password-688' })); }); const sensitiveRecording = await callBridge(bridgeSocket, 'verify-recording-sensitive-get', 'browser.recording.get', { tabId: targetTab.id }); if (!JSON.stringify(sensitiveRecording.result).includes('recorder-sensitive-preview-686')) { throw new Error(`Explicit recording value capture did not return its bounded preview: ${JSON.stringify(sensitiveRecording)}`); } const callableSource = sensitiveRecording.result?.events?.find((item) => ( item.kind === 'crypto' && item.crypto?.adapterId === 'jsencrypt' && item.crypto?.operation === 'encrypt' && item.callHandleId && item.callableCapable )); if (!callableSource) throw new Error(`Sensitive recording did not retain an executable JSEncrypt call handle: ${JSON.stringify(sensitiveRecording)}`); const createdCallable = await callBridge(bridgeSocket, 'verify-callable-create', 'browser.callable.create', { tabId: targetTab.id, source: 'recording', callHandleId: callableSource.callHandleId, name: 'E2E JSEncrypt RSA Callable', }); if (createdCallable.error || !createdCallable.result?.id) throw new Error(`Could not create a recorded page callable: ${JSON.stringify(createdCallable)}`); await callBridge(bridgeSocket, 'verify-recording-stop-sensitive', 'browser.recording.stop', { tabId: targetTab.id }); const replayedCallable = await callBridge(bridgeSocket, 'verify-callable-execute-after-stop', 'browser.callable.execute', { tabId: targetTab.id, callableId: createdCallable.result.id, args: [{ username: 'callable-rsa-admin-797', password: 'callable-rsa-password-798' }], }); const replayedRsaPlaintext = replayedCallable.result?.value ? decryptRSALabValue(replayedCallable.result.value) : undefined; const replayedRsaPayload = replayedRsaPlaintext ? JSON.parse(replayedRsaPlaintext) : undefined; if (replayedCallable.error || replayedRsaPayload?.username !== 'callable-rsa-admin-797' || replayedRsaPayload?.password !== 'callable-rsa-password-798') { throw new Error(`Document-bound JSEncrypt callable lost its receiver or input adaptation after recording stopped: ${JSON.stringify({ replayedCallable, replayedRsaPlaintext, replayedRsaPayload })}`); } await webPage.evaluate(() => { window.sendDigestWithNetwork = function sendDigestWithNetwork(value) { const encryptedData = window.CryptoJS.SHA256(value).toString(); return fetch('/api/session?source=deep-risk-classification', { method: 'POST', body: encryptedData, }); }; }); const riskyCaptureStart = await callBridge(bridgeSocket, 'verify-risky-capture-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: linkedCryptoEvent.crypto.adapterId, operation: linkedCryptoEvent.crypto.operation, wrapperHandleId: linkedCryptoEvent.wrapperHandleId, }, }); if (riskyCaptureStart.error || riskyCaptureStart.result?.state !== 'armed') { throw new Error(`Side-effect classification capture did not arm: ${JSON.stringify(riskyCaptureStart)}`); } await webPage.evaluate(() => setTimeout(() => void window.sendDigestWithNetwork('risk-classification-value'), 50)); let riskyPause; for (let attempt = 0; attempt < 60; attempt += 1) { const next = await callBridge(bridgeSocket, `verify-risky-capture-status-${attempt}`, 'browser.deep_capture.status', { tabId: targetTab.id }); if (next.error) throw new Error(`Side-effect classification status failed: ${JSON.stringify(next)}`); if (next.result?.state === 'paused') { riskyPause = next.result; await callBridge(bridgeSocket, `verify-risky-capture-keepalive-${attempt}`, 'browser.deep_capture.keepalive', { tabId: targetTab.id }); const frame = riskyPause.pause?.frames?.find((item) => item.functionName === 'sendDigestWithNetwork'); if (frame?.functionInspection) break; } await new Promise((resolveWait) => setTimeout(resolveWait, 100)); } const riskyFrame = riskyPause?.pause?.frames?.find((frame) => frame.functionName === 'sendDigestWithNetwork'); if (riskyFrame?.sourceKind !== 'page' || !riskyFrame.functionInspection?.resolved || !riskyFrame.functionInspection.riskFlags?.includes('network')) { throw new Error(`Network side effect was not classified on the business frame: ${JSON.stringify(riskyPause)}`); } if (riskyPause.pause?.automaticCapture?.state !== 'blocked') { throw new Error(`A network-sending business function was not blocked from automatic capture: ${JSON.stringify(riskyPause)}`); } const blockedRiskyCallable = await callBridge(bridgeSocket, 'verify-risky-callable-blocked', 'browser.callable.create', { tabId: targetTab.id, source: 'deep-capture', strategy: 'selected-frame', callFrameId: riskyFrame.id, }); if (blockedRiskyCallable.error?.code !== 'callable_capture_blocked') { throw new Error(`Side-effect gate allowed a network-sending callable: ${JSON.stringify(blockedRiskyCallable)}`); } await callBridge(bridgeSocket, 'verify-risky-capture-resume', 'browser.deep_capture.resume', { tabId: targetTab.id }); await callBridge(bridgeSocket, 'verify-risky-capture-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); const closureIsNotGlobal = await webPage.evaluate((functionName) => ( typeof window[functionName] === 'undefined' ), closureFunctionName); if (!closureIsNotGlobal) throw new Error('The randomized ESM holdout accidentally exposed its business function globally'); await webPage.locator('#opaque-module-input').fill(closureReplayMarker); const closureCaptureStart = await callBridge(bridgeSocket, 'verify-closure-capture-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: webCryptoDigestEvent.crypto.adapterId, operation: webCryptoDigestEvent.crypto.operation, wrapperHandleId: webCryptoDigestEvent.wrapperHandleId, }, }); if (closureCaptureStart.error || closureCaptureStart.result?.state !== 'armed') { throw new Error(`Randomized ESM/WASM closure capture did not arm: ${JSON.stringify(closureCaptureStart)}`); } await webPage.evaluate(() => { setTimeout(() => document.querySelector('#opaque-module-submit').click(), 50); }); let closurePause; for (let attempt = 0; attempt < 80; attempt += 1) { const next = await callBridge(bridgeSocket, `verify-closure-capture-status-${attempt}`, 'browser.deep_capture.status', { tabId: targetTab.id }); if (next.error) throw new Error(`Randomized ESM/WASM capture status failed: ${JSON.stringify(next)}`); if (next.result?.state === 'paused') { closurePause = next.result; await callBridge(bridgeSocket, `verify-closure-capture-keepalive-${attempt}`, 'browser.deep_capture.keepalive', { tabId: targetTab.id }); const frame = closurePause.pause?.frames?.find((item) => item.functionName === closureFunctionName); if (!closurePause.pause?.collecting && frame?.functionInspection) break; } await new Promise((resolveWait) => setTimeout(resolveWait, 100)); } const closureFrame = closurePause?.pause?.frames?.find((frame) => frame.functionName === closureFunctionName); const closureVariables = closureFrame?.scopes?.flatMap((scope) => scope.variables) || []; if (!closureFrame || closureFrame.sourceKind !== 'page' || !closureFrame.url.includes(closureModulePath) || !closureFrame.functionInspection?.resolved || closureFrame.functionInspection.riskFlags?.length || !closureVariables.some((variable) => variable.name === 'instance') || closurePause.pause?.recommendedFrameId !== closureFrame.id || closurePause.pause?.automaticCapture?.state !== 'ready' || closurePause.pause?.automaticCapture?.frameId !== closureFrame.id) { throw new Error(`Randomized ESM/WASM business frame was not recovered deterministically: ${JSON.stringify(closurePause)}`); } const closureCallable = await callBridge(bridgeSocket, 'verify-closure-callable-create', 'browser.callable.create', { tabId: targetTab.id, source: 'deep-capture', strategy: 'selected-frame', callFrameId: closureFrame.id, name: 'E2E opaque module envelope', }); if (closureCallable.error || closureCallable.result?.kind !== 'business-closure') { throw new Error(`Randomized ESM/WASM callable could not be retained: ${JSON.stringify(closureCallable)}`); } await webPage.locator('#opaque-module-result').getByText(closureReplayMarker, { exact: true }).waitFor(); const closureReplay = await callBridge(bridgeSocket, 'verify-closure-callable-execute', 'browser.callable.execute', { tabId: targetTab.id, callableId: closureCallable.result.id, args: [{ marker: closureReplayMarker, nested: { seed: closureHoldoutSeed } }], }); if (closureReplay.error || !closureReplay.result?.value || typeof closureReplay.result.value !== 'object') { throw new Error(`Randomized ESM/WASM callable replay failed: ${JSON.stringify(closureReplay)}`); } const closureServerResponse = await fetch(new URL(closureSubmitPath, testUrl), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(closureReplay.result.value), }); const closureServerResult = await closureServerResponse.json(); if (!closureServerResponse.ok || !closureServerResult.ok || closureServerResult.marker !== closureReplayMarker) { throw new Error(`Independent server rejected the retained ESM/WASM callable: ${JSON.stringify(closureServerResult)}`); } await callBridge(bridgeSocket, 'verify-closure-capture-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); await webPage.locator('#crypto-lab[data-ready="true"]').waitFor(); await webPage.locator('#crypto-password').fill('deep-capture-first-901'); const deepCaptureStart = await callBridge(bridgeSocket, 'verify-deep-capture-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: webCryptoEncryptEvent.crypto.adapterId, operation: webCryptoEncryptEvent.crypto.operation, wrapperHandleId: webCryptoEncryptEvent.wrapperHandleId, }, }); if (deepCaptureStart.error || deepCaptureStart.result?.state !== 'armed') { throw new Error(`Deep capture did not arm: ${JSON.stringify(deepCaptureStart)}`); } await webPage.evaluate(() => { setTimeout(() => document.querySelector('#crypto-submit').click(), 50); }); let deepPause; let lastDeepStatus; for (let attempt = 0; attempt < 80; attempt += 1) { const next = await callBridge(bridgeSocket, `verify-deep-capture-status-${attempt}`, 'browser.deep_capture.status', { tabId: targetTab.id }); if (next.error) throw new Error(`Deep capture status failed: ${JSON.stringify(next)}`); lastDeepStatus = next.result; if (next.result?.state === 'paused') { deepPause = next.result; await callBridge(bridgeSocket, `verify-deep-capture-keepalive-${attempt}`, 'browser.deep_capture.keepalive', { tabId: targetTab.id }); const frame = deepPause.pause?.frames?.find((item) => item.functionName === 'buildLoginEnvelope'); const variables = frame?.scopes?.flatMap((scope) => scope.variables) || []; if (!deepPause.pause?.collecting && variables.some((variable) => variable.name === 'password')) break; } await new Promise((resolveWait) => setTimeout(resolveWait, 100)); } const businessFrame = deepPause?.pause?.frames?.find((frame) => frame.functionName === 'buildLoginEnvelope'); const businessVariables = businessFrame?.scopes?.flatMap((scope) => scope.variables) || []; if (!businessFrame || businessFrame.sourceKind !== 'page' || deepPause?.pause?.frames?.[0]?.sourceKind !== 'extension-hook' || !businessFrame.scopes.some((scope) => scope.type === 'closure' || scope.type === 'local') || !businessFrame.functionInspection?.resolved || businessFrame.functionInspection.riskFlags?.length || !businessVariables.some((variable) => variable.name === 'password' && variable.preview.includes('deep-capture-first-901'))) { throw new Error(`Deep capture did not expose the real business frame and lexical inputs: ${JSON.stringify({ deepPause, lastDeepStatus })}`); } await options.getByRole('button', { name: '网络活动' }).click(); await options.locator('#deep-mode-tab').click(); const pausedDeepWorkbench = options.locator('.deep-paused-workbench'); await pausedDeepWorkbench.waitFor(); await options.locator('.deep-stack button.is-selected').getByText('buildLoginEnvelope', { exact: true }).waitFor(); await options.locator('.deep-stack').getByText('插件 Hook', { exact: true }).first().waitFor(); await options.locator('.deep-stack').getByText('页面函数', { exact: true }).first().waitFor(); const expandablePassword = options.locator('.deep-scope-variable').filter({ hasText: 'password' }).first(); await expandablePassword.locator(':scope > button').click(); await expandablePassword.locator('pre').getByText('deep-capture-first-901', { exact: true }).waitFor(); if (await expandablePassword.locator(':scope > button').getAttribute('aria-expanded') !== 'true') { throw new Error('Deep capture scope row did not expose its inline value block'); } const pausedDeepBounds = await pausedDeepWorkbench.evaluate((element) => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, })); if (pausedDeepBounds.scrollWidth > pausedDeepBounds.clientWidth) { throw new Error(`Paused deep workbench overflowed horizontally: ${JSON.stringify(pausedDeepBounds)}`); } await options.screenshot({ path: resolve(artifacts, 'options-deep-capture-paused.png') }); const capturedCallable = await callBridge(bridgeSocket, 'verify-captured-callable-create', 'browser.callable.create', { tabId: targetTab.id, source: 'deep-capture', strategy: 'selected-frame', callFrameId: businessFrame.id, name: 'E2E login envelope', }); if (capturedCallable.error || capturedCallable.result?.provenance?.functionName !== 'buildLoginEnvelope') { throw new Error(`Could not retain the real business closure: ${JSON.stringify(capturedCallable)}`); } await options.locator('#recording-mode-tab').click(); await webPage.locator('#crypto-result').getByText('deep-capture-first-901').waitFor(); await webPage.locator('#crypto-password').fill('deep-capture-response-906'); const responseCaptureStart = await callBridge(bridgeSocket, 'verify-response-capture-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: webCryptoDecryptEvent.crypto.adapterId, operation: webCryptoDecryptEvent.crypto.operation, wrapperHandleId: webCryptoDecryptEvent.wrapperHandleId, }, }); if (responseCaptureStart.error || responseCaptureStart.result?.state !== 'armed') { throw new Error(`Response decrypt capture did not arm: ${JSON.stringify(responseCaptureStart)}`); } await webPage.evaluate(() => { setTimeout(() => document.querySelector('#crypto-submit').click(), 50); }); let responsePause; for (let attempt = 0; attempt < 80; attempt += 1) { const next = await callBridge(bridgeSocket, `verify-response-capture-status-${attempt}`, 'browser.deep_capture.status', { tabId: targetTab.id }); if (next.error) throw new Error(`Response decrypt capture status failed: ${JSON.stringify(next)}`); if (next.result?.state === 'paused') { responsePause = next.result; await callBridge(bridgeSocket, `verify-response-capture-keepalive-${attempt}`, 'browser.deep_capture.keepalive', { tabId: targetTab.id }); if (responsePause.pause?.frames?.some((frame) => frame.functionName === 'openLoginResponse')) break; } await new Promise((resolveWait) => setTimeout(resolveWait, 100)); } const responseBusinessFrame = responsePause?.pause?.frames?.find((frame) => frame.functionName === 'openLoginResponse'); if (!responseBusinessFrame) { throw new Error(`Deep capture did not expose the real response decrypt function: ${JSON.stringify(responsePause)}`); } const responseCallable = await callBridge(bridgeSocket, 'verify-response-callable-create', 'browser.callable.create', { tabId: targetTab.id, source: 'deep-capture', strategy: 'selected-frame', callFrameId: responseBusinessFrame.id, name: 'E2E login response decryptor', }); if (responseCallable.error || responseCallable.result?.provenance?.functionName !== 'openLoginResponse') { throw new Error(`Could not retain the real response decrypt closure: ${JSON.stringify(responseCallable)}`); } await webPage.locator('#crypto-result').getByText('deep-capture-response-906').waitFor(); const transformProfile = await callBridge(bridgeSocket, 'verify-transform-profile-save', 'browser.transform.profile.save', { name: 'E2E login plaintext gateway', enabled: true, target: { tabId: targetTab.id, frameId: 0 }, origin: new URL(testUrl).origin, match: { methods: ['POST'], urlPattern: '*/crypto-lab/submit' }, request: { enabled: true, nodes: [ { id: 'request-password', name: 'Read password', kind: 'context.read', path: 'body.password' }, { id: 'request-account', name: 'Read account', kind: 'context.read', path: 'body.account' }, { id: 'build-login-envelope', name: 'Build real login envelope', kind: 'page.call', callableId: capturedCallable.result.id, arguments: [{ nodeId: 'request-password' }, { nodeId: 'request-account' }], }, { id: 'write-login-envelope', name: 'Write encrypted envelope', kind: 'output.write', source: { nodeId: 'build-login-envelope' }, destination: 'body', encoding: 'json', }, ], }, response: { enabled: true, nodes: [ { id: 'response-body', name: 'Read response body', kind: 'context.read', path: 'body' }, { id: 'open-login-response', name: 'Open real login response', kind: 'page.call', callableId: responseCallable.result.id, arguments: [{ nodeId: 'response-body' }], }, { id: 'write-plaintext-response', name: 'Write plaintext response', kind: 'output.write', source: { nodeId: 'open-login-response' }, destination: 'body', encoding: 'json', }, ], }, failMode: 'closed', maxConcurrency: 1, }); if (transformProfile.error || !transformProfile.result?.id || !transformProfile.result?.target?.documentId) { throw new Error(`Could not bind the plaintext gateway to the live document: ${JSON.stringify(transformProfile)}`); } const plaintextGatewayBody = { account: 'gateway-operator', password: 'gateway-plaintext-906' }; const transformedRequest = await callBridge(bridgeSocket, 'verify-transform-request', 'browser.transform.execute', { profileId: transformProfile.result.id, direction: 'request', packet: { method: 'POST', url: new URL('/crypto-lab/submit', testUrl).href, headers: [{ name: 'Content-Type', value: 'application/json' }], bodyBase64: Buffer.from(JSON.stringify(plaintextGatewayBody)).toString('base64'), }, }); if (transformedRequest.error || transformedRequest.result?.profileId !== transformProfile.result.id) { throw new Error(`Plaintext gateway request transform failed: ${JSON.stringify(transformedRequest)}`); } const wireEnvelope = JSON.parse(Buffer.from(transformedRequest.result.bodyBase64, 'base64').toString('utf8')); if (!wireEnvelope.ciphertext || !wireEnvelope.signature || JSON.stringify(wireEnvelope).includes(plaintextGatewayBody.password)) { throw new Error(`Plaintext gateway did not produce a real encrypted wire body: ${JSON.stringify(wireEnvelope)}`); } const wireServerResponse = await fetch(new URL('/crypto-lab/submit', testUrl), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(wireEnvelope), }).then((response) => response.json()); if (!wireServerResponse.ciphertext || JSON.stringify(wireServerResponse).includes(plaintextGatewayBody.password)) { throw new Error(`Real server did not accept the request and return an encrypted response: ${JSON.stringify(wireServerResponse)}`); } const transformedResponse = await callBridge(bridgeSocket, 'verify-transform-response', 'browser.transform.execute', { profileId: transformProfile.result.id, direction: 'response', packet: { method: 'POST', url: new URL('/crypto-lab/submit', testUrl).href, statusCode: 200, headers: [{ name: 'Content-Type', value: 'application/json' }], bodyBase64: Buffer.from(JSON.stringify(wireServerResponse)).toString('base64'), }, }); const plaintextServerResponse = transformedResponse.error ? undefined : JSON.parse(Buffer.from(transformedResponse.result.bodyBase64, 'base64').toString('utf8')); if (!plaintextServerResponse?.ok || plaintextServerResponse.password !== plaintextGatewayBody.password || plaintextServerResponse.account !== plaintextGatewayBody.account) { throw new Error(`Plaintext gateway did not restore the real encrypted server response: ${JSON.stringify({ transformedResponse, plaintextServerResponse })}`); } const rejectedTransformRoute = await callBridge(bridgeSocket, 'verify-transform-route-closed', 'browser.transform.execute', { profileId: transformProfile.result.id, direction: 'request', packet: { method: 'POST', url: new URL('/api/session', testUrl).href, headers: [], bodyBase64: Buffer.from(JSON.stringify(plaintextGatewayBody)).toString('base64'), }, }); if (rejectedTransformRoute.error?.code !== 'transform_route_mismatch') { throw new Error(`Plaintext gateway did not fail closed on a mismatched route: ${JSON.stringify(rejectedTransformRoute)}`); } const rejectedTransformOrigin = await callBridge(bridgeSocket, 'verify-transform-origin-closed', 'browser.transform.execute', { profileId: transformProfile.result.id, direction: 'request', packet: { method: 'POST', url: `http://localhost:${address.port}/crypto-lab/submit`, headers: [], bodyBase64: Buffer.from(JSON.stringify(plaintextGatewayBody)).toString('base64'), }, }); if (rejectedTransformOrigin.error?.code !== 'transform_route_mismatch') { throw new Error(`Path-only plaintext gateway route crossed the bound origin: ${JSON.stringify(rejectedTransformOrigin)}`); } const visibleTransformProfiles = await callBridge(bridgeSocket, 'verify-transform-profile-list', 'browser.transform.profile.list', { tabId: targetTab.id, }); if (visibleTransformProfiles.error || !visibleTransformProfiles.result?.some((profile) => profile.id === transformProfile.result.id)) { throw new Error(`Authorized transform profile was not visible to Yakit: ${JSON.stringify(visibleTransformProfiles)}`); } const callableExecutionA = await callBridge(bridgeSocket, 'verify-callable-execute-a', 'browser.callable.execute', { tabId: targetTab.id, callableId: capturedCallable.result.id, args: ['deep-capture-replay-902', 'automation-a'], }); const callableExecutionB = await callBridge(bridgeSocket, 'verify-callable-execute-b', 'browser.callable.execute', { tabId: targetTab.id, callableId: capturedCallable.result.id, args: ['deep-capture-replay-903', 'automation-b'], }); if (callableExecutionA.error || callableExecutionB.error || callableExecutionA.result?.value?.nonce === callableExecutionB.result?.value?.nonce || callableExecutionA.result?.value?.iv === callableExecutionB.result?.value?.iv) { throw new Error(`Page callable did not preserve dynamic browser behavior: ${JSON.stringify({ callableExecutionA, callableExecutionB })}`); } const callableWireResponse = await fetch(new URL('/crypto-lab/submit', testUrl), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(callableExecutionA.result.value), }).then((response) => response.json()); const callableValidation = await decryptCryptoLabResponse(callableWireResponse); if (!callableValidation.ok || callableValidation.password !== 'deep-capture-replay-902' || callableValidation.account !== 'automation-a') { throw new Error(`Captured page callable did not pass server validation: ${JSON.stringify(callableValidation)}`); } const detachedDeepCapture = await callBridge(bridgeSocket, 'verify-deep-capture-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); if (detachedDeepCapture.error || detachedDeepCapture.result?.state !== 'detached') { throw new Error(`Deep capture did not detach cleanly: ${JSON.stringify(detachedDeepCapture)}`); } const deepRecordingSnapshot = await callBridge( bridgeSocket, 'verify-deep-recording-source', 'browser.recording.get', { tabId: targetTab.id, limit: 100 }, ); const filteredSource = [...(deepRecordingSnapshot.result?.events || [])].reverse() .find((event) => event.kind === 'crypto' && event.crypto?.adapterId === 'webcrypto' && event.crypto?.operation === 'encrypt' && event.scriptUrl && event.wrapperHandleId); if (!filteredSource?.scriptUrl || !filteredSource.wrapperHandleId) { throw new Error(`Deep capture did not retain a script source for filtered re-arm: ${JSON.stringify(deepRecordingSnapshot)}`); } await webPage.locator('#crypto-password').fill('deep-filter-match-904'); const filteredCaptureStart = await callBridge(bridgeSocket, 'verify-deep-filter-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: filteredSource.crypto.adapterId, operation: filteredSource.crypto.operation, wrapperHandleId: filteredSource.wrapperHandleId, scriptUrl: filteredSource.scriptUrl, }, }); if (filteredCaptureStart.error || filteredCaptureStart.result?.state !== 'armed') { throw new Error(`Script-filtered deep capture did not arm: ${JSON.stringify(filteredCaptureStart)}`); } await webPage.evaluate(() => setTimeout(() => document.querySelector('#crypto-submit').click(), 50)); let filteredPause; for (let attempt = 0; attempt < 60; attempt += 1) { const next = await callBridge(bridgeSocket, `verify-deep-filter-status-${attempt}`, 'browser.deep_capture.status', { tabId: targetTab.id }); if (next.result?.state === 'paused') { filteredPause = next.result; break; } await new Promise((resolveWait) => setTimeout(resolveWait, 100)); } if (!filteredPause) throw new Error(`Script-filtered deep capture did not pause at its source: ${filteredSource.scriptUrl}`); const filteredResume = await callBridge(bridgeSocket, 'verify-deep-filter-resume', 'browser.deep_capture.resume', { tabId: targetTab.id }); if (filteredResume.error) throw new Error(`Script-filtered deep capture did not resume: ${JSON.stringify(filteredResume)}`); await webPage.locator('#crypto-result').getByText('deep-filter-match-904').waitFor(); await callBridge(bridgeSocket, 'verify-deep-filter-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); await webPage.locator('#crypto-password').fill('deep-filter-miss-905'); const missedCaptureStart = await callBridge(bridgeSocket, 'verify-deep-filter-miss-start', 'browser.deep_capture.start', { tabId: targetTab.id, matcher: { kind: 'crypto', adapterId: filteredSource.crypto.adapterId, operation: filteredSource.crypto.operation, wrapperHandleId: filteredSource.wrapperHandleId, scriptUrl: 'https://not-the-calling-script.invalid/no-match.js', }, }); if (missedCaptureStart.error) throw new Error(`Non-matching deep capture did not arm: ${JSON.stringify(missedCaptureStart)}`); await webPage.evaluate(() => setTimeout(() => document.querySelector('#crypto-submit').click(), 50)); await webPage.locator('#crypto-result').getByText('deep-filter-miss-905').waitFor(); const missedCaptureStatus = await callBridge(bridgeSocket, 'verify-deep-filter-miss-status', 'browser.deep_capture.status', { tabId: targetTab.id }); if (missedCaptureStatus.error || missedCaptureStatus.result?.state !== 'armed') { throw new Error(`A non-matching script incorrectly consumed the deep breakpoint: ${JSON.stringify(missedCaptureStatus)}`); } await callBridge(bridgeSocket, 'verify-deep-filter-miss-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); const idleRecorderDurationMs = await webPage.evaluate(() => { const started = performance.now(); let checksum = 0; for (let index = 0; index < 1_000; index += 1) { checksum += window.CryptoJS.SHA256(`idle-${index}`).sigBytes; } if (checksum !== 32_000) throw new Error('Idle recorder benchmark changed page return values'); return performance.now() - started; }); const performanceRecordingStart = await callBridge( bridgeSocket, 'verify-recorder-performance-start', 'browser.recording.start', { tabId: targetTab.id, captureValues: false, maxEntries: 20 }, ); if (performanceRecordingStart.error) { throw new Error(`Recorder performance session did not start: ${JSON.stringify(performanceRecordingStart)}`); } const recorderPerformance = await webPage.evaluate(() => { const smallStarted = performance.now(); let checksum = 0; for (let index = 0; index < 1_000; index += 1) { checksum += window.CryptoJS.SHA256(`active-${index}`).sigBytes; } const smallCallsMs = performance.now() - smallStarted; const largePayload = 'x'.repeat(1024 * 1024); const largeStarted = performance.now(); for (let index = 0; index < 10; index += 1) { checksum += window.CryptoJS.SHA256(largePayload).sigBytes; } const largeCallsMs = performance.now() - largeStarted; const oversizedPayload = 'y'.repeat(3 * 1024 * 1024); checksum += window.CryptoJS.SHA256(oversizedPayload).sigBytes; if (checksum !== 32_352) throw new Error('Active recorder benchmark changed page return values'); return { smallCallsMs, largeCallsMs }; }); const performanceSnapshot = await callBridge( bridgeSocket, 'verify-recorder-performance-get', 'browser.recording.get', { tabId: targetTab.id, limit: 20 }, ); const oversizedEvent = performanceSnapshot.result?.events?.find((item) => item.byteLength === 3 * 1024 * 1024); if (performanceSnapshot.error || performanceSnapshot.result?.status?.count !== 20 || performanceSnapshot.result?.status?.droppedCount < 991 || oversizedEvent?.callHandleId || oversizedEvent?.callableCapable) { throw new Error(`Recorder budgets were not enforced under load: ${JSON.stringify({ status: performanceSnapshot.result?.status, oversizedEvent, })}`); } if (recorderPerformance.smallCallsMs > 3_000 || recorderPerformance.largeCallsMs > 8_000) { throw new Error(`Recorder performance gate exceeded: ${JSON.stringify({ idleRecorderDurationMs, ...recorderPerformance })}`); } const performanceRecordingStop = await callBridge( bridgeSocket, 'verify-recorder-performance-stop', 'browser.recording.stop', { tabId: targetTab.id }, ); if (performanceRecordingStop.error || performanceRecordingStop.result?.status?.active) { throw new Error(`Recorder performance session did not stop cleanly: ${JSON.stringify(performanceRecordingStop)}`); } const observerRestored = await webPage.evaluate(() => ({ fetch: window.fetch === window.__yakitObserverOriginals.fetch, xhrOpen: XMLHttpRequest.prototype.open === window.__yakitObserverOriginals.xhrOpen, xhrSend: XMLHttpRequest.prototype.send === window.__yakitObserverOriginals.xhrSend, webSocket: window.WebSocket === window.__yakitObserverOriginals.webSocket, worker: window.Worker === window.__yakitObserverOriginals.worker, workerPostMessage: window.Worker?.prototype.postMessage === window.__yakitObserverOriginals.workerPostMessage, messageChannel: window.MessageChannel === window.__yakitObserverOriginals.messageChannel, messagePortPostMessage: window.MessagePort?.prototype.postMessage === window.__yakitObserverOriginals.messagePortPostMessage, sendBeacon: Navigator.prototype.sendBeacon === window.__yakitObserverOriginals.sendBeacon, digest: Object.getPrototypeOf(crypto.subtle).digest === window.__yakitObserverOriginals.digest, cryptoJs: window.CryptoJS.SHA256 === window.__yakitObserverOriginals.cryptoJsSha256, jsencrypt: window.JSEncrypt.prototype.encrypt === window.__yakitObserverOriginals.jsencryptEncrypt, sm2: window.sm2.doEncrypt === window.__yakitObserverOriginals.sm2Encrypt, sm3: window.sm3 === window.__yakitObserverOriginals.sm3, sm4: window.sm4.encrypt === window.__yakitObserverOriginals.sm4Encrypt, forgeCipher: window.forge.cipher.createCipher === window.__yakitObserverOriginals.forgeCreateCipher, forgePki: window.forge.pki.publicKeyFromPem === window.__yakitObserverOriginals.forgePublicKeyFromPem, forgeDigest: window.forge.md.sha256.create === window.__yakitObserverOriginals.forgeSha256Create, forgeHmac: window.forge.hmac.create === window.__yakitObserverOriginals.forgeHmacCreate, })); if (Object.values(observerRestored).some((value) => !value)) throw new Error(`Page APIs were not restored after recording: ${JSON.stringify(observerRestored)}`); const insecurePage = await context.newPage(); await insecurePage.goto(insecureTestUrl); const insecureTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))) .find((tab) => tab.url === insecureTestUrl); if (!insecureTab?.id) throw new Error('Could not resolve the insecure HTTP test tab'); const insecureEnvironment = await insecurePage.evaluate(() => ({ isSecureContext, randomUUID: typeof crypto.randomUUID, getRandomValues: typeof crypto.getRandomValues, cacheStorage: typeof globalThis.caches, indexedDB: typeof globalThis.indexedDB, })); if (insecureEnvironment.isSecureContext || insecureEnvironment.randomUUID !== 'undefined' || insecureEnvironment.getRandomValues !== 'function' || insecureEnvironment.cacheStorage !== 'undefined') { throw new Error(`Insecure HTTP test did not reproduce the required Crypto environment: ${JSON.stringify(insecureEnvironment)}`); } const insecureStorageFixture = await insecurePage.evaluate(async () => { localStorage.setItem('yakit-insecure-local', 'local-session-value'); sessionStorage.setItem('yakit-insecure-session', 'tab-session-value'); if (!globalThis.indexedDB || typeof globalThis.indexedDB.databases !== 'function') return { indexedDB: false }; await new Promise((resolveDatabase, rejectDatabase) => { const open = globalThis.indexedDB.open('yakit-insecure-auth', 1); open.onupgradeneeded = () => open.result.createObjectStore('sessions'); open.onerror = () => rejectDatabase(open.error); open.onsuccess = () => { const database = open.result; const transaction = database.transaction('sessions', 'readwrite'); transaction.objectStore('sessions').put({ authenticated: true }, 'insecure-account'); transaction.oncomplete = () => { database.close(); resolveDatabase(); }; transaction.onerror = () => rejectDatabase(transaction.error); }; }); return { indexedDB: true }; }); const insecureContext = await options.evaluate(async (tabId) => { return await chrome.runtime.sendMessage({ action: 'context.capture', payload: { tabId, includeDom: false, includeStorage: true, includeCookies: false }, }); }, insecureTab.id); const insecureDocument = insecureContext?.data?.document; const insecureIndexedDatabase = insecureDocument?.storageInventory?.indexedDB?.databases ?.find((database) => database.name === 'yakit-insecure-auth'); if (!insecureContext?.ok || !insecureContext.data?.captureId || insecureDocument?.localStorage?.supported !== true || insecureDocument.localStorage.entries.find((entry) => entry.key === 'yakit-insecure-local')?.value !== 'local-session-value' || insecureDocument?.sessionStorage?.supported !== true || insecureDocument.sessionStorage.entries.find((entry) => entry.key === 'yakit-insecure-session')?.value !== 'tab-session-value' || insecureDocument?.storageInventory?.cacheStorage?.supported !== false || (insecureStorageFixture.indexedDB && !insecureIndexedDatabase?.stores?.some((store) => store.name === 'sessions' && store.sampleKeys.includes('insecure-account')))) { throw new Error(`Insecure HTTP structured context did not degrade storage capabilities independently: ${JSON.stringify(insecureContext)}`); } await insecurePage.evaluate(() => { window.CryptoJS = { SHA256(value) { return { sigBytes: 32, toString: () => `insecure-sha256:${value}` }; }, }; window.__yakitInsecureObserverOriginals = { fetch: window.fetch, xhrOpen: XMLHttpRequest.prototype.open, xhrSend: XMLHttpRequest.prototype.send, webSocket: window.WebSocket, cryptoJsSha256: window.CryptoJS.SHA256, }; }); const insecureRecordingStart = await options.evaluate(async (tabId) => { return await chrome.runtime.sendMessage({ action: 'recording.start', payload: { tabId, captureValues: false, maxEntries: 100 }, }); }, insecureTab.id); if (!insecureRecordingStart?.ok || insecureRecordingStart.data?.status?.active !== true) { throw new Error(`Insecure HTTP recording did not start: ${JSON.stringify(insecureRecordingStart)}`); } const insecureOriginalResults = await insecurePage.evaluate(async (socketPort) => { const cryptoJs = window.CryptoJS.SHA256('insecure-cryptojs-value').toString(); const fetchResponse = await fetch('/api/session?source=insecure-fetch', { method: 'POST', body: 'insecure-fetch-value' }); await new Promise((resolveRequest, rejectRequest) => { const request = new XMLHttpRequest(); request.open('POST', '/api/session?source=insecure-xhr'); request.onload = resolveRequest; request.onerror = rejectRequest; request.send('insecure-xhr-value'); }); const webSocket = await new Promise((resolveSocket, rejectSocket) => { const socket = new WebSocket(`ws://yakit-insecure.test:${socketPort}/page-socket`); socket.onopen = () => socket.send('insecure-websocket-value'); socket.onmessage = (event) => { const value = event.data; socket.close(); resolveSocket(value); }; socket.onerror = rejectSocket; }); return { cryptoJs, fetchStatus: fetchResponse.status, webSocket }; }, address.port); if (insecureOriginalResults.cryptoJs !== 'insecure-sha256:insecure-cryptojs-value' || insecureOriginalResults.fetchStatus !== 200 || insecureOriginalResults.webSocket !== 'echo:insecure-websocket-value') { throw new Error(`Recording changed insecure page behavior: ${JSON.stringify(insecureOriginalResults)}`); } const insecureRecording = await options.evaluate(async (tabId) => { return await chrome.runtime.sendMessage({ action: 'recording.get', payload: { tabId, limit: 100 } }); }, insecureTab.id); if (!insecureRecording?.ok) throw new Error(`Could not read insecure HTTP recording: ${JSON.stringify(insecureRecording)}`); const insecureKinds = new Set(insecureRecording.data.events.map((item) => item.kind)); for (const kind of ['fetch', 'xhr', 'websocket', 'crypto']) { if (!insecureKinds.has(kind)) throw new Error(`Insecure HTTP recording missed ${kind}: ${JSON.stringify(insecureRecording)}`); } const insecureRecordingStop = await options.evaluate(async (tabId) => { return await chrome.runtime.sendMessage({ action: 'recording.stop', payload: { tabId } }); }, insecureTab.id); if (!insecureRecordingStop?.ok) throw new Error(`Could not stop insecure HTTP recording: ${JSON.stringify(insecureRecordingStop)}`); const insecureRestored = await insecurePage.evaluate(() => ({ fetch: window.fetch === window.__yakitInsecureObserverOriginals.fetch, xhrOpen: XMLHttpRequest.prototype.open === window.__yakitInsecureObserverOriginals.xhrOpen, xhrSend: XMLHttpRequest.prototype.send === window.__yakitInsecureObserverOriginals.xhrSend, webSocket: window.WebSocket === window.__yakitInsecureObserverOriginals.webSocket, cryptoJs: window.CryptoJS.SHA256 === window.__yakitInsecureObserverOriginals.cryptoJsSha256, })); if (Object.values(insecureRestored).some((value) => !value)) { throw new Error(`Insecure HTTP page APIs were not restored: ${JSON.stringify(insecureRestored)}`); } await insecurePage.close(); const bridgeEval = await callBridge(bridgeSocket, 'verify-control-eval', 'browser.eval', { tabId: targetTab.id, mode: 'expression', timeoutMs: 5_000, code: `Promise.resolve({ answer: 6 * 7, encrypted: window.app.crypto.encrypt('sensitive-e2e-value-239') })`, }); if (bridgeEval.error || bridgeEval.result?.value?.answer !== 42 || bridgeEval.result?.value?.encrypted !== 'page:sensitive-e2e-value-239') { throw new Error(`Control grant browser.eval failed: ${JSON.stringify(bridgeEval)}`); } const cancelledResponsePromise = nextBridgeResponse(bridgeSocket, 'verify-cancelled-eval'); bridgeSocket.send(JSON.stringify({ id: 'verify-cancelled-eval', type: 'request', method: 'browser.eval', params: { tabId: targetTab.id, mode: 'expression', timeoutMs: 5_000, code: `new Promise((resolve) => setTimeout(() => resolve('late'), 1000))` }, })); await new Promise((resolveWait) => setTimeout(resolveWait, 50)); bridgeSocket.send(JSON.stringify({ id: 'verify-cancelled-eval', type: 'cancel' })); const cancelledBridgeEval = await cancelledResponsePromise; if (cancelledBridgeEval.error?.code !== 'cancelled') throw new Error(`Bridge cancellation was not enforced: ${JSON.stringify(cancelledBridgeEval)}`); const firstContext = await callBridge(bridgeSocket, 'verify-context-first', 'browser.context', { tabId: targetTab.id, includeDom: true, includeStorage: true, includeCookies: false, }); const accountNode = firstContext.result?.document?.interactive?.find((node) => node.name === 'account'); const indexedDatabase = firstContext.result?.document?.storageInventory?.indexedDB?.databases?.find((database) => database.name === 'yakit-e2e-auth'); if (firstContext.error || !firstContext.result?.captureId || !accountNode?.nodeId || 'html' in (firstContext.result?.document || {}) || firstContext.result.document.bodyText.length > 20 * 1024 || firstContext.result.frames.length < 3 || !indexedDatabase?.stores?.some((store) => store.name === 'sessions' && store.sampleKeys.includes('account-1')) || !firstContext.result.document.storageInventory.cacheStorage.names.includes('yakit-e2e-session-cache') || !firstContext.result.lifecycle.some((event) => event.kind === 'history')) { throw new Error(`Structured browser context is invalid or unbounded: ${JSON.stringify(firstContext)}`); } const inspectedAccount = await callBridge(bridgeSocket, 'verify-node-inspect', 'browser.node.inspect', { tabId: targetTab.id, captureId: firstContext.result.captureId, nodeId: accountNode.nodeId, }); if (inspectedAccount.error || inspectedAccount.result?.reference?.captureId !== firstContext.result.captureId || inspectedAccount.result?.attributes?.value) { throw new Error(`Stable node inspection leaked a value or returned the wrong reference: ${JSON.stringify(inspectedAccount)}`); } const setNodeValue = await callBridge(bridgeSocket, 'verify-node-set-value', 'browser.node.action', { tabId: targetTab.id, captureId: firstContext.result.captureId, nodeId: accountNode.nodeId, action: 'setValue', value: 'context-node-secret-e2e-448', }); if (setNodeValue.error || await webPage.locator('input[name="account"]').inputValue() !== 'context-node-secret-e2e-448') { throw new Error(`Stable node setValue failed: ${JSON.stringify(setNodeValue)}`); } await webPage.evaluate(() => { const button = document.createElement('button'); button.id = 'context-diff-action'; button.textContent = 'Context diff action'; button.addEventListener('click', () => { window.__contextNodeClicked = true; }); document.querySelector('main')?.append(button); }); const secondContext = await callBridge(bridgeSocket, 'verify-context-second', 'browser.context', { tabId: targetTab.id, includeDom: true, }); const addedActionNode = secondContext.result?.document?.interactive?.find((node) => node.nodeId && node.accessibleName === 'Context diff action'); if (secondContext.error || secondContext.result?.diff?.kind !== 'changed' || !secondContext.result.diff.addedNodes?.some((node) => node.text === 'Context diff action') || !addedActionNode) { throw new Error(`Context diff did not report the added action: ${JSON.stringify(secondContext)}`); } const staleNode = await callBridge(bridgeSocket, 'verify-stale-node', 'browser.node.inspect', { tabId: targetTab.id, captureId: firstContext.result.captureId, nodeId: accountNode.nodeId, }); if (staleNode.error?.code !== 'stale_node') throw new Error(`Old context node reference remained valid: ${JSON.stringify(staleNode)}`); const clickNode = await callBridge(bridgeSocket, 'verify-node-click', 'browser.node.action', { tabId: targetTab.id, captureId: secondContext.result.captureId, nodeId: addedActionNode.nodeId, action: 'click', }); await webPage.waitForTimeout(50); if (clickNode.error || await webPage.evaluate(() => window.__contextNodeClicked) !== true) { throw new Error(`Stable node click failed: ${JSON.stringify(clickNode)}`); } await webPage.evaluate(() => { const container = document.createElement('div'); container.id = 'context-performance-fixture'; const fragment = document.createDocumentFragment(); for (let index = 0; index < 10_500; index += 1) { const span = document.createElement('span'); span.textContent = `安全上下文-${index} `; fragment.append(span); } container.append(fragment); document.body.append(container); }); const boundedContext = await callBridge(bridgeSocket, 'verify-context-bounds', 'browser.context', { tabId: targetTab.id, includeDom: true, }); if (boundedContext.error || boundedContext.result?.document?.scannedElementCount !== 10_000 || !boundedContext.result.document.limitsReached?.includes('scanned_elements') || Buffer.byteLength(boundedContext.result.document.bodyText, 'utf8') > 20 * 1024) { throw new Error(`Structured context did not enforce scan/text byte limits: ${JSON.stringify(boundedContext)}`); } await webPage.evaluate(() => document.getElementById('context-performance-fixture')?.remove()); const handoffResponse = await callBridge(bridgeSocket, 'verify-handoff', 'browser.handoff.request', { tabId: targetTab.id, reason: 'qr_code', message: '请扫描测试二维码并确认登录', }); if (handoffResponse.error || handoffResponse.result?.state !== 'waiting_for_user') { throw new Error(`Human handoff did not enter waiting state: ${JSON.stringify(handoffResponse)}`); } await options.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); await floatingFrame.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-human-handoff.png') }); await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-handoff.png') }); const handoffPopup = await context.newPage(); await handoffPopup.setViewportSize({ width: 390, height: 560 }); await handoffPopup.goto(`chrome-extension://${extensionId}/popup.html`); await handoffPopup.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); await handoffPopup.screenshot({ path: resolve(artifacts, 'popup-human-handoff.png') }); await handoffPopup.close(); const handoffEventPromise = nextBridgeEvent(bridgeSocket, 'browser.handoff.changed'); await floatingFrame.getByRole('button', { name: '已完成' }).click(); const handoffEvent = await handoffEventPromise; if (handoffEvent.params?.id !== handoffResponse.result.id || handoffEvent.params?.state !== 'completed') { throw new Error(`Engine did not receive completed handoff event: ${JSON.stringify(handoffEvent)}`); } await options.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor({ state: 'detached' }); await options.getByRole('button', { name: '操作记录' }).click(); await options.getByText('browser.handoff.request', { exact: true }).first().waitFor(); await options.waitForTimeout(300); await options.screenshot({ path: resolve(artifacts, 'options-activity.png') }); const auditEvents = await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'audit.list', payload: { limit: 200 } }); if (!response?.ok) throw new Error(response?.error || 'audit.list'); return response.data; }); if (JSON.stringify(auditEvents).includes('sensitive-e2e-value-239') || JSON.stringify(auditEvents).includes('context-node-secret-e2e-448')) throw new Error('Audit log leaked browser.eval or node action data'); if (!auditEvents.some((event) => event.action === 'handoff.completed' && event.outcome === 'success')) throw new Error('Audit log is missing the completed handoff'); const runtimeAndStorage = await options.evaluate(async () => { const runtimeResponse = await chrome.runtime.sendMessage({ action: 'agent.runtime.get' }); if (!runtimeResponse?.ok) throw new Error(runtimeResponse?.error || 'agent.runtime.get'); return { runtime: runtimeResponse.data, local: await chrome.storage.local.get(null), session: await chrome.storage.session.get(null), }; }); if (runtimeAndStorage.runtime.state !== 'running' || !runtimeAndStorage.runtime.actions.some((action) => action.method === 'browser.handoff.request' && action.state === 'success')) { throw new Error(`Agent runtime timeline is incomplete: ${JSON.stringify(runtimeAndStorage.runtime)}`); } for (const key of ['settings.proxy.v1', 'settings.user-agent.v1', 'settings.bridge.v2', 'ui.floating-panel.v1']) { if (!(key in runtimeAndStorage.local)) throw new Error(`Split local storage key is missing: ${key}`); } if ('yakit-extension-state-v5' in runtimeAndStorage.local || JSON.stringify(runtimeAndStorage.local).includes('proxy-session-secret-818')) { throw new Error('Local storage retained a legacy state blob or session-only proxy password'); } for (const key of ['session.browser-agent.v1', 'session.bridge.v1', 'session.agent-runtime.v1']) { if (!(key in runtimeAndStorage.session)) throw new Error(`Split session storage key is missing: ${key}`); } const automaticCaptureStartedAt = Date.now(); const automaticCaptureRecording = await callBridge( bridgeSocket, 'verify-automatic-business-capture-recording-start', 'browser.recording.start', { tabId: targetTab.id, captureValues: true, maxEntries: 80 }, ); if (automaticCaptureRecording.error) { throw new Error(`Could not start the automatic business capture recording: ${JSON.stringify(automaticCaptureRecording)}`); } await webPage.locator('#crypto-password').fill('automatic-capture-seed-951'); await webPage.locator('#crypto-submit').click(); await webPage.locator('#crypto-result').getByText('automatic-capture-seed-951').waitFor(); const automaticCaptureSnapshot = await callBridge( bridgeSocket, 'verify-automatic-business-capture-recording-get', 'browser.recording.get', { tabId: targetTab.id, limit: 80 }, ); const automaticCandidate = automaticCaptureSnapshot.result?.profileCandidates?.find((candidate) => ( candidate.status === 'capture-required' && candidate.sources?.length >= 2 && candidate.request?.url?.includes('/crypto-lab/submit') )); const commonBusinessHint = automaticCandidate?.capturePlan?.frameHints?.find((hint) => ( hint.functionName === 'buildLoginEnvelope' )); if (!automaticCandidate || automaticCandidate.capturePlan?.matcherEventId !== automaticCandidate.source.eventId || !commonBusinessHint || commonBusinessHint.support < 2) { throw new Error(`Multi-call inference did not identify a common business ancestor: ${JSON.stringify(automaticCaptureSnapshot)}`); } await options.getByRole('button', { name: '网络活动' }).click(); await options.locator('#recording-mode-tab').click(); await options.getByRole('button', { name: '刷新录制', exact: true }).click(); const automaticInference = options.locator('.profile-inference').filter({ hasText: '/crypto-lab/submit' }); await automaticInference.waitFor(); await automaticInference.getByRole('button', { name: '自动捕获完整加密流程', exact: true }).click(); await options.locator('#deep-mode-tab[aria-selected="true"]').waitFor(); await options.getByText('等待目标页面命中', { exact: true }).waitFor(); await webPage.locator('#crypto-password').fill('automatic-capture-replay-952'); await webPage.locator('#crypto-submit').click(); await options.locator('#gateway-mode-tab[aria-selected="true"]').waitFor({ timeout: 30_000 }); const automaticGateway = options.locator('.transform-guide'); await automaticGateway.waitFor(); if (await automaticGateway.getByLabel('输出形态').inputValue() !== 'body') { throw new Error('Automatic multi-call capture did not generate a whole-envelope gateway'); } const automaticInputs = automaticGateway.locator('.transform-guide-inputs > div'); if (await automaticInputs.count() !== 2 || await automaticInputs.nth(0).locator('select').inputValue() !== 'body-field' || await automaticInputs.nth(0).locator('input').inputValue() !== 'password' || await automaticInputs.nth(1).locator('select').inputValue() !== 'body-field' || await automaticInputs.nth(1).locator('input').inputValue() !== 'account') { throw new Error('Automatic business capture did not create parameter-level input bindings'); } const automaticReplayBody = JSON.parse(await options.getByLabel('回放 Body').inputValue()); if (automaticReplayBody.password !== 'automatic-capture-replay-952' || automaticReplayBody.account !== 'analyst') { throw new Error(`Automatic business capture did not preserve its paused local sample: ${JSON.stringify(automaticReplayBody)}`); } const automaticSampleLabel = await options.locator('.transform-test-field-label em').getAttribute('title'); if (!automaticSampleLabel?.includes('buildLoginEnvelope')) { throw new Error(`Automatic business capture did not identify the replay sample source: ${automaticSampleLabel}`); } const automaticCallables = await callBridge( bridgeSocket, 'verify-automatic-business-capture-callables', 'browser.callable.list', { tabId: targetTab.id }, ); const automaticCallable = automaticCallables.result?.findLast((callable) => ( callable.kind === 'business-closure' && callable.provenance?.functionName === 'buildLoginEnvelope' && callable.createdAt >= automaticCaptureStartedAt )); if (automaticCallables.error || !automaticCallable?.id || automaticCallable.inputSlots?.length !== 2 || automaticCallable.inputSlots[0]?.name !== 'password' || automaticCallable.inputSlots[1]?.name !== 'account' || automaticCallable.operation !== 'buildLoginEnvelope') { throw new Error(`One-click capture did not retain the complete business callable: ${JSON.stringify(automaticCallables)}`); } await options.locator('.transform-editor-actions').getByRole('button', { name: '保存', exact: true }).click(); const executeAutomaticPipeline = options.getByRole('button', { name: '执行 Pipeline', exact: true }); await executeAutomaticPipeline.waitFor({ state: 'visible' }); await executeAutomaticPipeline.click(); await options.getByText('转换完成', { exact: true }).waitFor(); const automaticLogicalOutput = JSON.parse(await options.locator('.transform-test-result pre').textContent()); if (!automaticLogicalOutput.body?.ciphertext || !automaticLogicalOutput.body?.signature || !automaticLogicalOutput.body?.iv) { throw new Error(`Automatic business gateway did not execute the complete page closure: ${JSON.stringify(automaticLogicalOutput)}`); } const automaticProfiles = await callBridge( bridgeSocket, 'verify-automatic-business-profile-list', 'browser.transform.profile.list', { tabId: targetTab.id }, ); const automaticProfile = automaticProfiles.result?.find((profile) => ( profile.createdAt >= automaticCaptureStartedAt && profile.request?.nodes?.some((node) => node.kind === 'page.call' && node.callableId === automaticCallable.id) )); if (automaticProfiles.error || !automaticProfile?.id) { throw new Error(`Automatic business gateway was not persisted for execution: ${JSON.stringify(automaticProfiles)}`); } const removedAutomaticProfile = await callBridge( bridgeSocket, 'verify-automatic-business-profile-delete', 'browser.transform.profile.delete', { id: automaticProfile.id }, ); if (removedAutomaticProfile.error || removedAutomaticProfile.result?.some((profile) => profile.id === automaticProfile.id)) { throw new Error(`Automatic business gateway test profile was not removed: ${JSON.stringify(removedAutomaticProfile)}`); } await webPage.locator('#crypto-result').getByText('automatic-capture-replay-952').waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-automatic-business-capture.png') }); await callBridge(bridgeSocket, 'verify-automatic-business-recording-stop', 'browser.recording.stop', { tabId: targetTab.id }); await callBridge(bridgeSocket, 'verify-automatic-business-capture-detach', 'browser.deep_capture.detach', { tabId: targetTab.id }); const guidedFormRecordingStart = await callBridge(bridgeSocket, 'verify-guided-form-recording-start', 'browser.recording.start', { tabId: targetTab.id, captureValues: true, maxEntries: 40, }); if (guidedFormRecordingStart.error) { throw new Error(`Could not start the guided form recording: ${JSON.stringify(guidedFormRecordingStart)}`); } await webPage.evaluate(async () => { const plaintext = JSON.stringify({ username: 'admin', password: '123456' }); const encryptedData = window.__yakitRsa.encrypt(plaintext); const response = await fetch('/encrypt/rsa.php?source=guided-rsa-profile', { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: new URLSearchParams({ data: encryptedData }), }); const validation = await response.json(); if (!validation.ok || validation.plaintext !== plaintext) { throw new Error(`Guided RSA request was not accepted by the independent server: ${JSON.stringify(validation)}`); } }); const guidedFormRecording = await callBridge(bridgeSocket, 'verify-guided-form-recording-get', 'browser.recording.get', { tabId: targetTab.id, limit: 40, }); const guidedFormCandidate = guidedFormRecording.result?.profileCandidates?.find((candidate) => ( candidate.status === 'ready' && candidate.request?.serialization === 'form-field' && candidate.request?.destination === 'body.data' && candidate.source?.crypto?.adapterId === 'jsencrypt' )); if (!guidedFormCandidate?.source?.eventId) { throw new Error(`Dedicated recording did not produce a guided form candidate: ${JSON.stringify(guidedFormRecording)}`); } await options.getByRole('button', { name: '网络活动' }).click(); await options.locator('#gateway-mode-tab').click(); const transformWorkbench = options.locator('.transform-workbench'); await transformWorkbench.waitFor(); await options.getByText('E2E login plaintext gateway', { exact: true }).waitFor(); const transformBounds = await transformWorkbench.evaluate((element) => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, })); if (transformBounds.scrollWidth > transformBounds.clientWidth) { throw new Error(`Plaintext gateway workbench overflowed horizontally: ${JSON.stringify(transformBounds)}`); } await options.screenshot({ path: resolve(artifacts, 'options-browser-transform-gateway.png') }); await options.locator('#recording-mode-tab').click(); await options.getByRole('button', { name: '刷新录制', exact: true }).click(); const recordingWorkbench = options.locator('.recording-workbench'); await recordingWorkbench.waitFor(); await recordingWorkbench.scrollIntoViewIfNeeded(); const recordingBounds = await recordingWorkbench.evaluate((element) => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, })); if (recordingBounds.scrollWidth > recordingBounds.clientWidth) { throw new Error(`Recording workbench overflowed horizontally: ${JSON.stringify(recordingBounds)}`); } const inferredProfilePanel = options.locator('.profile-inference'); await inferredProfilePanel.waitFor(); const inferredProfileHeading = await inferredProfilePanel.locator('.profile-inference__heading strong').innerText(); if (!/JSEncrypt/.test(inferredProfileHeading) || /base64\.encode/i.test(inferredProfileHeading)) { throw new Error(`Profile inference promoted an encoding helper instead of the crypto source: ${inferredProfileHeading}`); } await inferredProfilePanel.getByText(/高置信度/).waitFor(); await options.getByText('验证页面函数', { exact: true }).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-browser-recording.png') }); await options.locator(`[data-event-id="${guidedFormCandidate.source.eventId}"]`).click(); const formInferencePanel = options.locator('.profile-inference'); await formInferencePanel.locator('.profile-inference__heading strong').getByText(/body\.data/).waitFor(); await options.getByRole('button', { name: '停止录制并保存', exact: true }).click(); const createRecordedCallable = options.getByRole('button', { name: '创建', exact: true }); await createRecordedCallable.waitFor(); await options.waitForTimeout(500); await createRecordedCallable.click(); await options.getByText('验证页面函数', { exact: true }).waitFor(); const guidedCallableList = await callBridge( bridgeSocket, 'verify-guided-callable-list', 'browser.callable.list', { tabId: targetTab.id }, ); const guidedCallableForDelete = guidedCallableList.result?.find((callable) => ( callable.provenance?.eventId === guidedFormCandidate.source.eventId )); if (guidedCallableList.error || !guidedCallableForDelete?.name) { throw new Error(`The UI-created page callable was not retained: ${JSON.stringify(guidedCallableList)}`); } if (guidedCallableForDelete.crypto?.adapterId !== 'jsencrypt' || guidedCallableForDelete.crypto?.key?.bits !== 1024 || guidedCallableForDelete.crypto?.padding !== 'PKCS1-v1_5') { throw new Error(`The UI-created callable lost its RSA adapter metadata: ${JSON.stringify(guidedCallableForDelete)}`); } const guidedRsaReplay = await callBridge(bridgeSocket, 'verify-guided-rsa-replay', 'browser.callable.execute', { tabId: targetTab.id, callableId: guidedCallableForDelete.id, args: [{ username: 'guided-rsa-admin', password: 'guided-rsa-password' }], }); const guidedRsaReplayPlaintext = guidedRsaReplay.result?.value ? decryptRSALabValue(guidedRsaReplay.result.value) : undefined; const guidedRsaReplayPayload = guidedRsaReplayPlaintext ? JSON.parse(guidedRsaReplayPlaintext) : undefined; if (guidedRsaReplay.error || guidedRsaReplayPayload?.username !== 'guided-rsa-admin' || guidedRsaReplayPayload?.password !== 'guided-rsa-password') { throw new Error(`The UI-created RSA callable could not replay through its retained receiver: ${JSON.stringify({ guidedRsaReplay, guidedRsaReplayPlaintext, guidedRsaReplayPayload })}`); } await formInferencePanel.getByRole('button', { name: '生成明文网关', exact: true }).click(); const guidedGateway = options.locator('.transform-guide'); await guidedGateway.waitFor(); if (await guidedGateway.getByLabel('输出形态').inputValue() !== 'form-field' || await guidedGateway.getByLabel('表单字段名').inputValue() !== 'data') { throw new Error('Form evidence was not compiled into a guided form-field gateway'); } if (await options.getByLabel('URL 模式').inputValue() !== '*/encrypt/rsa.php' || !await options.getByLabel('回放请求 URL').inputValue().then((value) => value.startsWith(`${new URL(testUrl).origin}/encrypt/rsa.php?`))) { throw new Error('Relative request evidence was not resolved against the current page'); } await guidedGateway.getByText('自动设置表单 Content-Type', { exact: true }).waitFor(); if (await options.locator('.transform-node-list').isVisible()) { throw new Error('Guided gateway leaked the advanced DAG editor'); } const replayBody = await options.getByLabel('回放 Body').inputValue(); if (JSON.stringify(JSON.parse(replayBody)) !== JSON.stringify({ username: 'admin', password: '123456' })) { throw new Error(`Recorded short sample was not carried into local replay: ${replayBody}`); } await options.locator('.transform-test-field-label').getByText('短时样本', { exact: true }).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-browser-transform-guided-rsa.png') }); await options.locator('.transform-callable-menu > summary').click(); const disposableCallable = options.locator('.transform-callable-list section').filter({ hasText: guidedCallableForDelete.name }); await disposableCallable.getByRole('button', { name: `删除 ${guidedCallableForDelete.name}`, exact: true }).click(); await disposableCallable.getByRole('button', { name: '确认删除', exact: true }).click(); await disposableCallable.waitFor({ state: 'detached' }); await options.screenshot({ path: resolve(artifacts, 'options-browser-transform-callables.png') }); await options.locator('#recording-mode-tab').click(); await options.locator('#deep-mode-tab').click(); const deepCaptureWorkspace = options.locator('.deep-capture'); await deepCaptureWorkspace.waitFor(); await deepCaptureWorkspace.getByText('E2E login envelope', { exact: true }).waitFor(); const deepCaptureBounds = await deepCaptureWorkspace.evaluate((element) => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, })); if (deepCaptureBounds.scrollWidth > deepCaptureBounds.clientWidth) { throw new Error(`Deep capture workbench overflowed horizontally: ${JSON.stringify(deepCaptureBounds)}`); } await options.screenshot({ path: resolve(artifacts, 'options-deep-capture.png') }); await options.locator('#recording-mode-tab').click(); await options.locator('.network-control-bar .ui-switch').nth(0).click(); await options.locator('.network-control-bar .ui-switch').nth(1).click(); await options.getByRole('button', { name: '开始捕获' }).click(); await webPage.evaluate(async () => { const response = await fetch('/api/session?source=browser-agent', { method: 'POST', headers: { 'content-type': 'application/json', 'x-yakit-e2e': 'request-header-value' }, body: JSON.stringify({ marker: 'network-sensitive-e2e-771' }), }); if (!response.ok) throw new Error(`E2E API returned ${response.status}`); await response.json(); }); const capturedNetworkRow = options.locator('.network-row').filter({ hasText: '/api/session?source=browser-agent' }).first(); await capturedNetworkRow.waitFor(); await capturedNetworkRow.click(); await options.getByText(/x-yakit-e2e: request-header-value/i).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-network-activity.png') }); const bridgeNetworkList = await callBridge(bridgeSocket, 'verify-network-list', 'browser.network.list', { tabId: targetTab.id, limit: 20, }); const capturedRequest = bridgeNetworkList.result?.find((record) => record.url?.includes('/api/session')); if (bridgeNetworkList.error || !capturedRequest?.requestHeaders?.some((header) => header.name.toLowerCase() === 'x-yakit-e2e') || capturedRequest.requestBody?.data?.includes('network-sensitive-e2e-771') !== true) { throw new Error(`Agent network capture did not include the explicitly granted request: ${JSON.stringify(bridgeNetworkList)}`); } await options.getByRole('button', { name: 'Yakit' }).click(); const fuzzerOpenMessage = await webFuzzerOpenRequest; const fuzzerPacket = Buffer.from(fuzzerOpenMessage.params?.rawRequestBase64 || '', 'base64').toString('utf8'); if (!fuzzerPacket.includes('POST /api/session?source=browser-agent HTTP/1.1') || !/x-yakit-e2e: request-header-value/i.test(fuzzerPacket) || !fuzzerPacket.includes('network-sensitive-e2e-771') || !/cookie: .*yakit_e2e_session=authenticated/i.test(fuzzerPacket)) { throw new Error(`Web Fuzzer handoff did not contain the real authenticated request: ${fuzzerPacket}`); } await options.getByRole('button', { name: 'PoC' }).click(); const pocMessage = await pocGenerateRequest; const pocPacket = Buffer.from(pocMessage.params?.rawRequestBase64 || '', 'base64').toString('utf8'); if (!pocPacket.includes('network-sensitive-e2e-771')) throw new Error(`Yak PoC generation did not receive the captured request: ${pocPacket}`); await options.getByText('browser-e2e.yak', { exact: true }).waitFor(); await options.getByRole('button', { name: '分析', exact: true }).click(); const analysisMessage = await analysisPrepareRequest; if (JSON.stringify(analysisMessage.params?.observations || []).includes('recorder-sensitive-preview-686')) { throw new Error('AI analysis payload included a sensitive recording preview'); } await options.getByText('AI 分析上下文', { exact: true }).waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-network-analysis.png') }); const networkAudit = await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'audit.list', payload: { limit: 200 } }); if (!response?.ok) throw new Error(response?.error || 'audit.list'); return response.data; }); if (JSON.stringify(networkAudit).includes('network-sensitive-e2e-771') || JSON.stringify(networkAudit).includes('yakit_e2e_session=authenticated')) { throw new Error('Audit log leaked captured network credentials or request body'); } await options.getByRole('button', { name: '停止' }).click(); await options.getByRole('button', { name: '登录态工作区' }).click(); await options.locator('.context-options input').nth(0).check(); await options.locator('.context-options input').nth(1).check(); await options.getByRole('button', { name: '采集页面' }).click(); await webPage.evaluate(() => { const button = document.createElement('button'); button.id = 'context-ui-diff'; button.textContent = 'UI context change'; document.querySelector('main')?.append(button); }); await options.getByRole('button', { name: '刷新并比较' }).click(); await options.getByText('发现变化', { exact: true }).first().waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-context-workspace.png') }); const evalResponse = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'context.eval', payload: { tabId, mode: 'program', timeoutMs: 5_000, code: `window.__yakitEvalMarker = 41; return Promise.resolve({ answer: window.__yakitEvalMarker + 1, encrypted: window.app.crypto.encrypt('ok'), host: location.hostname })`, }, }); }, { tabId: targetTab.id }); if (!evalResponse?.ok || evalResponse.data?.value?.answer !== 42 || evalResponse.data?.value?.encrypted !== 'page:ok') { throw new Error(`Unexpected page Eval response: ${JSON.stringify(evalResponse)}`); } const timeoutResponse = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'context.eval', payload: { tabId, mode: 'expression', timeoutMs: 250, code: `new Promise((resolve) => setTimeout(() => resolve('late'), 500))` }, }); }, { tabId: targetTab.id }); if (timeoutResponse?.ok || !timeoutResponse?.error?.includes('250ms')) { throw new Error(`Page Eval timeout was not enforced: ${JSON.stringify(timeoutResponse)}`); } const panelMetrics = await webPage.locator('.floating-panel').evaluate((panel) => { const rect = panel.getBoundingClientRect(); return { width: rect.width, height: rect.height, left: rect.left, right: rect.right, viewportWidth: innerWidth, viewportHeight: innerHeight }; }); if (panelMetrics.left < 0 || panelMetrics.right > panelMetrics.viewportWidth || panelMetrics.height > 480 || panelMetrics.height > panelMetrics.viewportHeight) { throw new Error(`Floating panel is outside the viewport: ${JSON.stringify(panelMetrics)}`); } const mobileOptions = await context.newPage(); await mobileOptions.setViewportSize({ width: 390, height: 844 }); await mobileOptions.goto(`chrome-extension://${extensionId}/options.html?tabId=${targetTab.id}#overview`); await mobileOptions.locator('.app-shell').waitFor(); await mobileOptions.waitForTimeout(300); const mobileOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); if (mobileOverflow > 0) throw new Error(`Options mobile layout overflows by ${mobileOverflow}px`); await mobileOptions.screenshot({ path: resolve(artifacts, 'options-mobile.png'), fullPage: true }); await mobileOptions.getByRole('button', { name: '网络活动' }).click(); await mobileOptions.waitForTimeout(300); const mobileNetworkOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); if (mobileNetworkOverflow > 0) throw new Error(`Network mobile layout overflows by ${mobileNetworkOverflow}px`); await mobileOptions.screenshot({ path: resolve(artifacts, 'options-network-mobile.png'), fullPage: true }); await mobileOptions.locator('#gateway-mode-tab').click(); const mobileTransformWorkbench = mobileOptions.locator('.transform-workbench'); await mobileTransformWorkbench.waitFor(); await mobileOptions.getByText('E2E login plaintext gateway', { exact: true }).waitFor(); await mobileOptions.waitForTimeout(250); const mobileTransformBounds = await mobileTransformWorkbench.evaluate((element) => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, viewportOverflow: document.documentElement.scrollWidth - innerWidth, })); if (mobileTransformBounds.viewportOverflow > 0 || mobileTransformBounds.scrollWidth > mobileTransformBounds.clientWidth) { throw new Error(`Plaintext gateway mobile layout overflowed: ${JSON.stringify(mobileTransformBounds)}`); } await mobileOptions.screenshot({ path: resolve(artifacts, 'options-browser-transform-gateway-mobile.png'), fullPage: true }); await mobileOptions.locator('#deep-mode-tab').click(); await mobileOptions.locator('.deep-capture').waitFor(); await mobileOptions.waitForTimeout(250); const mobileDeepOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); if (mobileDeepOverflow > 0) throw new Error(`Deep capture mobile layout overflows by ${mobileDeepOverflow}px`); await mobileOptions.screenshot({ path: resolve(artifacts, 'options-deep-capture-mobile.png'), fullPage: true }); await mobileOptions.locator('#recording-mode-tab').click(); await mobileOptions.getByRole('button', { name: '登录态工作区' }).click(); await mobileOptions.getByRole('button', { name: '采集页面' }).click(); await mobileOptions.locator('.context-session-strip').waitFor(); const mobileContextOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); if (mobileContextOverflow > 0) throw new Error(`Context mobile layout overflows by ${mobileContextOverflow}px`); await mobileOptions.screenshot({ path: resolve(artifacts, 'options-context-mobile.png'), fullPage: true }); await mobileOptions.setViewportSize({ width: 320, height: 700 }); await mobileOptions.goto(`chrome-extension://${extensionId}/options.html?tabId=${targetTab.id}#overview`); await mobileOptions.locator('.task-command-bar').waitFor(); const narrowOptionsLayout = await mobileOptions.evaluate(() => ({ overflow: document.documentElement.scrollWidth - innerWidth, wrappedNavLabels: [...document.querySelectorAll('.sidebar nav button span')].filter((label) => { const style = getComputedStyle(label); return label.getBoundingClientRect().height > Number.parseFloat(style.lineHeight || '20') * 1.5; }).length, })); if (narrowOptionsLayout.overflow > 0 || narrowOptionsLayout.wrappedNavLabels > 0) throw new Error(`Options 320px navigation/layout failed: ${JSON.stringify(narrowOptionsLayout)}`); await mobileOptions.screenshot({ path: resolve(artifacts, 'options-overview-320.png'), fullPage: true }); const narrowPage = await context.newPage(); await narrowPage.setViewportSize({ width: 320, height: 640 }); await narrowPage.goto(testUrl); await narrowPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); await narrowPage.locator('.floating-panel__brand').click(); await narrowPage.waitForTimeout(250); const narrowPanel = await narrowPage.locator('.floating-panel').evaluate((panel) => { const rect = panel.getBoundingClientRect(); return { width: rect.width, left: rect.left, right: rect.right, viewportWidth: innerWidth }; }); if (narrowPanel.left < 0 || narrowPanel.right > narrowPanel.viewportWidth) throw new Error(`Narrow floating panel overflows: ${JSON.stringify(narrowPanel)}`); await narrowPage.screenshot({ path: resolve(artifacts, 'floating-panel-narrow.png') }); const agentCapture = await callBridge(bridgeSocket, 'verify-agent-capture-start', 'browser.network.start', { tabId: targetTab.id, captureHeaders: false, captureBody: false, }); if (agentCapture.error || agentCapture.result?.active !== true) throw new Error(`Agent could not start a scoped capture: ${JSON.stringify(agentCapture)}`); const captureAfterRevoke = await options.evaluate(async ({ tabId }) => { const revoked = await chrome.runtime.sendMessage({ action: 'grant.revoke' }); if (!revoked?.ok) throw new Error(revoked?.error || 'grant.revoke'); const status = await chrome.runtime.sendMessage({ action: 'network.capture.status', payload: { tabId } }); if (!status?.ok) throw new Error(status?.error || 'network.capture.status'); return status.data; }, { tabId: targetTab.id }); if (captureAfterRevoke.active) throw new Error(`Agent-owned capture survived grant revocation: ${JSON.stringify(captureAfterRevoke)}`); await options.evaluate(async ({ tabId }) => { const response = await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }], scopes: [ 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', 'browser.proxy.read', 'browser.proxy.write', ], durationMinutes: 5, }, }); if (!response?.ok) throw new Error(response?.error || 'grant.create after capture revoke'); }, { tabId: targetTab.id }); await webPage.reload(); const staleDocumentEval = await callBridge(bridgeSocket, 'verify-document-boundary', 'browser.eval', { tabId: targetTab.id, mode: 'expression', code: 'document.title', }); if (staleDocumentEval.error?.code !== 'stale_document') throw new Error(`Grant survived a same-origin document reload: ${JSON.stringify(staleDocumentEval)}`); await options.evaluate(async ({ tabId }) => { const response = await chrome.runtime.sendMessage({ action: 'grant.create', payload: { targets: [{ tabId, frameId: 0 }], scopes: [ 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', 'browser.proxy.read', 'browser.proxy.write', ], durationMinutes: 5, }, }); if (!response?.ok) throw new Error(response?.error || 'grant.create after reload'); }, { tabId: targetTab.id }); await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: false } })); await webPage.locator('.floating-panel__brand').waitFor({ state: 'hidden' }); await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: true } })); await webPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); await webPage.evaluate(() => dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyY', altKey: true, shiftKey: true, bubbles: true }))); await webPage.locator('.floating-panel.is-expanded').waitFor(); await webPage.locator('.floating-panel__brand').click(); const currentOrigin = new URL(testUrl).origin; await options.evaluate(async (origin) => { const response = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { siteMode: 'denylist', siteOrigins: [origin] } }); if (!response?.ok) throw new Error(response?.error || 'panel.update denylist'); }, currentOrigin); await webPage.locator('.floating-panel__brand').waitFor({ state: 'hidden' }); await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { siteMode: 'all', siteOrigins: [] } }); if (!response?.ok) throw new Error(response?.error || 'panel.update all'); }); await webPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); const diagnostics = await options.evaluate(async () => { const response = await chrome.runtime.sendMessage({ action: 'diagnostics.export' }); if (!response?.ok) throw new Error(response?.error || 'diagnostics.export'); return response.data; }); const diagnosticsText = JSON.stringify(diagnostics); if (diagnosticsText.includes('sensitive-e2e-value-239') || diagnosticsText.includes('context-node-secret-e2e-448') || diagnosticsText.includes(testUrl)) { throw new Error('Diagnostics export leaked page, Eval, node or URL values'); } if (diagnostics.metrics.serviceWorkerStarts < 1 || diagnostics.metrics.heartbeatSamples < 1 || !diagnostics.metrics.capabilities['browser.context']) { throw new Error(`Diagnostics metrics are incomplete: ${JSON.stringify(diagnostics.metrics)}`); } const crossOriginUrl = testUrl.replace('127.0.0.1', 'localhost'); await webPage.goto(crossOriginUrl); const staleOriginEval = await callBridge(bridgeSocket, 'verify-origin-boundary', 'browser.eval', { tabId: targetTab.id, mode: 'expression', code: 'document.title', }); if (!staleOriginEval.error?.message?.includes('跨来源')) throw new Error(`Grant survived a cross-origin navigation: ${JSON.stringify(staleOriginEval)}`); const beforeRestart = await options.evaluate(async () => { const [state, runtime] = await Promise.all([ chrome.runtime.sendMessage({ action: 'state.get' }), chrome.runtime.sendMessage({ action: 'agent.runtime.get' }), ]); return { grantId: state.data?.activeGrant?.id, taskId: state.data?.activeGrant?.taskId, runtime: runtime.data }; }); const cdp = await context.newCDPSession(options); const versionPromise = new Promise((resolveVersion, rejectVersion) => { const timer = setTimeout(() => rejectVersion(new Error('Could not resolve extension Service Worker version')), 5_000); cdp.on('ServiceWorker.workerVersionUpdated', ({ versions }) => { const version = versions.find((item) => item.scriptURL.startsWith(`chrome-extension://${extensionId}/`) && item.runningStatus === 'running'); if (!version) return; clearTimeout(timer); resolveVersion(version); }); }); await cdp.send('ServiceWorker.enable'); const runningVersion = await versionPromise; await cdp.send('ServiceWorker.stopWorker', { versionId: runningVersion.versionId }); const afterRestart = await options.evaluate(async () => { const state = await chrome.runtime.sendMessage({ action: 'state.get' }); const runtime = await chrome.runtime.sendMessage({ action: 'agent.runtime.get' }); if (!state?.ok || !runtime?.ok) throw new Error(state?.error || runtime?.error || 'Service Worker restart state'); return { grantId: state.data?.activeGrant?.id, taskId: state.data?.activeGrant?.taskId, runtime: runtime.data }; }); await cdp.detach(); if (!beforeRestart.grantId || afterRestart.grantId !== beforeRestart.grantId || afterRestart.taskId !== beforeRestart.taskId || afterRestart.runtime.grantId !== beforeRestart.runtime.grantId) { throw new Error(`Service Worker restart lost grant/task runtime: ${JSON.stringify({ beforeRestart, afterRestart })}`); } await webPage.goto(testUrl); await webPage.locator('#crypto-lab[data-ready="true"]').waitFor(); await webPage.evaluate(() => { window.CryptoJS = { SHA256(value) { return { sigBytes: 32, toString: () => `sha256:${value}` }; }, }; }); const navigationRecordingStart = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'recording.start', payload: { tabId, frameId: 0, captureValues: true, maxEntries: 40, maxValueBytes: 8_192 }, }); }, { tabId: targetTab.id }); if (!navigationRecordingStart?.ok || navigationRecordingStart.data?.status?.active !== true) { throw new Error(`Could not start the navigation recording: ${JSON.stringify(navigationRecordingStart)}`); } await webPage.evaluate(() => { window.CryptoJS.SHA256(JSON.stringify({ username: 'redirect-admin', password: 'redirect-secret' })); }); const navigationSnapshotBefore = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'recording.get', payload: { tabId, frameId: 0, limit: 40 } }); }, { tabId: targetTab.id }); if (!navigationSnapshotBefore?.ok || !JSON.stringify(navigationSnapshotBefore.data).includes('redirect-admin')) { throw new Error(`Navigation recording did not cache the short sample: ${JSON.stringify(navigationSnapshotBefore)}`); } const recordingCompleteUrl = `${new URL(testUrl).origin}/recording-complete`; await webPage.goto(recordingCompleteUrl); await webPage.getByRole('heading', { name: 'Recording Complete Dashboard' }).waitFor(); let navigationSession; for (let attempt = 0; attempt < 40; attempt += 1) { const response = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'recording.get', payload: { tabId, frameId: 0, limit: 40 } }); }, { tabId: targetTab.id }); const navigationEvent = response?.data?.events?.find((item) => ( item.kind === 'navigation' && item.navigation?.toUrl === recordingCompleteUrl )); if (response?.ok && response.data?.status?.active === true && response.data?.status?.documentAvailable === true && ['committed', 'completed'].includes(navigationEvent?.navigation?.phase)) { navigationSession = response.data; break; } await options.waitForTimeout(100); } const recordedNavigation = navigationSession?.events?.find((item) => ( item.kind === 'navigation' && item.navigation?.toUrl === recordingCompleteUrl )); if (navigationSession?.status?.navigation?.toUrl !== recordingCompleteUrl || navigationSession.status.target?.tabId !== targetTab.id || !recordedNavigation || !JSON.stringify(navigationSession.events).includes('redirect-admin')) { throw new Error(`Recording did not continue across a same-tab navigation: ${JSON.stringify(navigationSession)}`); } await options.getByRole('button', { name: '网络活动' }).click(); await options.locator('#recording-mode-tab').click(); await options.getByRole('button', { name: '刷新录制', exact: true }).click(); await options.locator('.recording-pipeline-step.is-navigation').waitFor(); await options.getByText('页面跳转', { exact: true }).first().waitFor(); await options.getByText(/最早 ↓ 最新/).waitFor(); await options.waitForFunction(({ tabId }) => { const select = document.querySelector('[aria-label="目标标签页"]'); return select instanceof HTMLSelectElement && select.value === String(tabId) && select.selectedOptions[0]?.textContent === 'Recording Complete Dashboard'; }, { tabId: targetTab.id }); if (Number(await options.getByLabel('目标标签页').inputValue()) !== targetTab.id) { throw new Error('Same-tab navigation changed the selected browser target'); } await options.screenshot({ path: resolve(artifacts, 'options-browser-recording-navigation.png') }); await webPage.goBack(); await webPage.locator('#crypto-lab[data-ready="true"]').waitFor(); let restoredRecording; let lastRestoredRecordingResponse; for (let attempt = 0; attempt < 40; attempt += 1) { const response = await options.evaluate(async ({ tabId }) => { return await chrome.runtime.sendMessage({ action: 'recording.get', payload: { tabId, frameId: 0, limit: 40 } }); }, { tabId: targetTab.id }); lastRestoredRecordingResponse = response; const navigationEvents = response?.data?.events?.filter((item) => item.kind === 'navigation') || []; if (response?.ok && response.data?.status?.active === true && response.data?.status?.documentAvailable === true && navigationEvents.length >= 2 && response.data.status.navigation?.kind === 'back-forward') { restoredRecording = response.data; break; } await options.waitForTimeout(100); } if (!restoredRecording || !JSON.stringify(restoredRecording.events).includes('redirect-admin')) { throw new Error(`Browser Back did not restore the active recording session: ${JSON.stringify(lastRestoredRecordingResponse)}`); } await options.getByRole('button', { name: '刷新录制', exact: true }).click(); await options.waitForFunction(() => document.querySelectorAll('.recording-traces button').length >= 2); await options.locator('.recording-traces button').last().click(); await options.locator('.recording-pipeline-step.is-navigation').waitFor(); await options.screenshot({ path: resolve(artifacts, 'options-browser-recording-restored.png') }); const unpairedIdentity = await options.evaluate(async () => { const before = await chrome.runtime.sendMessage({ action: 'state.get' }); const unpaired = await chrome.runtime.sendMessage({ action: 'bridge.unpair' }); const after = await chrome.runtime.sendMessage({ action: 'state.get' }); if (!before?.ok || !unpaired?.ok || !after?.ok) throw new Error(before?.error || unpaired?.error || after?.error || 'bridge.unpair'); return { beforeInstallationId: before.data.bridge.installationId, afterInstallationId: after.data.bridge.installationId, pairedEngine: after.data.bridge.pairedEngine, autoConnect: after.data.bridge.autoConnect, }; }); if (!unpairedIdentity.beforeInstallationId || unpairedIdentity.afterInstallationId !== unpairedIdentity.beforeInstallationId || unpairedIdentity.pairedEngine !== undefined || unpairedIdentity.autoConnect !== false) { throw new Error(`Local unpair changed the stable browser installation identity: ${JSON.stringify(unpairedIdentity)}`); } if (browserErrors.length > 0) throw new Error(`Browser page errors:\n${browserErrors.join('\n')}`); console.log(JSON.stringify({ extensionId, testUrl, evalResult: evalResponse.data, timeoutError: timeoutResponse.error, bridgeEval: bridgeEval.result, cancelledBridgeError: cancelledBridgeEval.error, handoffEvent, auditEventCount: networkAudit.length, capturedNetworkUrl: capturedRequest.url, fuzzerPageId: 'e2e-fuzzer-page', protocolChecks, staleDocumentError: staleDocumentEval.error, staleOriginError: staleOriginEval.error?.message, serviceWorkerRestart: { beforeRestart, afterRestart }, unpairedIdentity, recorderPerformance: { idleCallsMs: idleRecorderDurationMs, ...recorderPerformance }, rightMetrics, panelMetrics, narrowPanel, artifacts }, null, 2)); } finally { await context?.close(); server.close(); for (const client of bridgeServer.clients) client.terminate(); for (const client of pairingServer.clients) client.terminate(); bridgeServer.close(); pairingServer.close(); bridgeHTTPServer.close(); await rm(userDataDir, { recursive: true, force: true }); }