'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const crypto = require('node:crypto'); const { APT_EXTENSION_ID, APT_EXTENSION_ORIGIN } = require('../src/bridge-auth'); const ROOT = path.join(__dirname, '..'); const EXTENSION = path.join(ROOT, 'chrome-extension'); const OLD_TOKEN = 'apt-local-' + 'bridge-token'; const EXPECTED_ID = 'onbkfpbggekakjddomjjnboippimlmch'; function read(name) { return fs.readFileSync(path.join(EXTENSION, name), 'utf8'); } function extensionIdFromKey(key) { const digest = crypto.createHash('sha256').update(Buffer.from(key, 'base64')).digest().subarray(0, 16); return [...digest].map((byte) => String.fromCharCode(97 + (byte >> 4), 97 + (byte & 0x0f)) ).join(''); } function makeElement() { const listeners = {}; return { textContent: '', className: '', disabled: false, hidden: false, checked: false, value: '', addEventListener(type, listener) { listeners[type] = listener; }, async dispatch(type) { return listeners[type]?.({ preventDefault() {} }); } }; } function makePopupHarness({ paired = true, confirmCopy = false, clipboardReject = null, validServerProof = true } = {}) { const elements = Object.fromEntries( ['tabInfo', 'pairingInfo', 'sendBtn', 'copyBtn', 'statusMsg', 'openOptionsBtn'] .map((id) => [id, makeElement()]) ); const clipboardWrites = []; const confirmationMessages = []; const fetchCalls = []; let cookieReads = 0; const chromeApi = { storage: { local: { get: async () => paired ? { aptPairingSecret: 'A'.repeat(43) } : {} } }, tabs: { query: async () => [{ url: 'https://customer.avasecurity.com/devices' }] }, cookies: { get: async () => { cookieReads += 1; return { value: 'sensitive-va-token' }; } }, runtime: { openOptionsPage() {} } }; const navigatorApi = { clipboard: { async writeText(value) { if (clipboardReject) throw clipboardReject; clipboardWrites.push(value); } } }; const documentApi = { getElementById: (id) => elements[id] }; const { createPopupController } = require('../chrome-extension/popup.js'); const controller = createPopupController({ chromeApi, documentApi, navigatorApi, confirmCopy: (message) => { confirmationMessages.push(message); return confirmCopy; }, cryptoApi: crypto.webcrypto, fetchImpl: async (...args) => { fetchCalls.push(args); if (args[0].endsWith('/challenge')) { const { clientNonce } = JSON.parse(args[1].body); const serverNonce = 'B'.repeat(43); const canonical = JSON.stringify(['apt-server-challenge-v1', clientNonce, serverNonce]); const serverProof = validServerProof ? crypto.createHmac('sha256', 'A'.repeat(43)).update(canonical).digest('base64url') : 'C'.repeat(43); return { ok: true, json: async () => ({ success: true, serverNonce, serverProof }) }; } return { ok: true, json: async () => ({ success: true }) }; } }); return { controller, elements, clipboardWrites, confirmationMessages, fetchCalls, get cookieReads() { return cookieReads; } }; } test('manifest commits only a public key, stable ID, local storage, and exact loopback host access', () => { const manifest = JSON.parse(read('manifest.json')); assert.equal(extensionIdFromKey(manifest.key), EXPECTED_ID); assert.equal(APT_EXTENSION_ID, EXPECTED_ID); assert.equal(APT_EXTENSION_ORIGIN, `chrome-extension://${EXPECTED_ID}`); assert.ok(manifest.permissions.includes('storage')); assert.equal(manifest.options_page, 'options.html'); assert.ok(manifest.host_permissions.includes('http://127.0.0.1:18247/*')); assert.equal(manifest.host_permissions.some((entry) => entry.includes('localhost')), false); assert.equal(/PRIVATE KEY/.test(manifest.key), false); }); test('extension source contains no old token and sends only to the exact bridge endpoint', () => { const sources = ['manifest.json', 'popup.js', 'popup.html', 'options.js', 'options.html'] .map(read).join('\n'); assert.equal(sources.includes(OLD_TOKEN), false); assert.match(read('popup.js'), /http:\/\/127\.0\.0\.1:18247\/cookie/); assert.doesNotMatch(sources, /http:\/\/(?:localhost|\[::1\]|0\.0\.0\.0):18247/); assert.doesNotMatch(sources, /console\.(?:log|debug|info)\s*\(/); }); test('popup stays disabled while unpaired and directs the user to pairing options', async () => { const harness = makePopupHarness({ paired: false }); await harness.controller.init(); assert.equal(harness.elements.sendBtn.disabled, true); assert.equal(harness.elements.copyBtn.disabled, true); assert.match(harness.elements.pairingInfo.textContent, /Not paired/i); assert.equal(harness.elements.openOptionsBtn.hidden, false); }); test('paired popup enables preserved actions only on a supported Alta HTTPS tab', async () => { const harness = makePopupHarness({ paired: true }); await harness.controller.init(); assert.equal(harness.elements.sendBtn.textContent || 'Send to APT', 'Send to APT'); assert.equal(harness.elements.copyBtn.textContent || 'Copy VA Token', 'Copy VA Token'); assert.equal(harness.elements.sendBtn.disabled, false); assert.equal(harness.elements.copyBtn.disabled, false); assert.match(harness.elements.pairingInfo.textContent, /Paired/i); }); test('deployment detection rejects non-HTTPS, bare, and lookalike Alta hosts', () => { const { isSupportedDeploymentUrl } = require('../chrome-extension/popup.js'); assert.equal(isSupportedDeploymentUrl('https://customer.avasecurity.com/path'), true); assert.equal(isSupportedDeploymentUrl('http://customer.avasecurity.com/path'), false); assert.equal(isSupportedDeploymentUrl('https://avasecurity.com/path'), false); assert.equal(isSupportedDeploymentUrl('https://customer.avasecurity.com.evil.test/path'), false); }); test('Send to APT authenticates the listener before reading or sending the cookie', async () => { const harness = makePopupHarness({ paired: true }); await harness.controller.init(); await harness.controller.sendToApt(); assert.equal(harness.fetchCalls.length, 2); const [challengeUrl, challengeRequest] = harness.fetchCalls[0]; assert.equal(challengeUrl, 'http://127.0.0.1:18247/challenge'); assert.equal(JSON.stringify(challengeRequest).includes('sensitive-va-token'), false); assert.equal(JSON.stringify(challengeRequest).includes('A'.repeat(43)), false); const [url, request] = harness.fetchCalls[1]; assert.equal(url, 'http://127.0.0.1:18247/cookie'); assert.equal(Object.keys(request.headers).some((name) => /pairing/i.test(name)), false); const body = JSON.parse(request.body); assert.equal(body.deploymentUrl, 'https://customer.avasecurity.com'); assert.equal(body.cookieValue, 'sensitive-va-token'); assert.match(body.clientNonce, /^[A-Za-z0-9_-]{43}$/); assert.equal(body.serverNonce, 'B'.repeat(43)); assert.match(body.proof, /^[A-Za-z0-9_-]{43}$/); assert.equal(request.body.includes('A'.repeat(43)), false); }); test('a port-squatting fake server with an invalid proof receives no cookie or pairing secret', async () => { const harness = makePopupHarness({ paired: true, validServerProof: false }); await harness.controller.init(); await harness.controller.sendToApt(); assert.equal(harness.fetchCalls.length, 1); assert.equal(harness.fetchCalls[0][0], 'http://127.0.0.1:18247/challenge'); assert.equal(harness.cookieReads, 0); const network = JSON.stringify(harness.fetchCalls); assert.equal(network.includes('sensitive-va-token'), false); assert.equal(network.includes('A'.repeat(43)), false); }); test('copy cancellation occurs before cookie access and never writes the token', async () => { const harness = makePopupHarness({ paired: true, confirmCopy: false }); await harness.controller.init(); await harness.controller.copyToken(); assert.equal(harness.cookieReads, 0); assert.deepEqual(harness.clipboardWrites, []); assert.match(harness.elements.statusMsg.textContent, /cancelled/i); }); test('confirmed copy uses one concise prompt and reports success without exposing token', async () => { const harness = makePopupHarness({ paired: true, confirmCopy: true }); await harness.controller.init(); await harness.controller.copyToken(); assert.deepEqual(harness.confirmationMessages, ['Copy VA token?']); assert.deepEqual(harness.clipboardWrites, ['sensitive-va-token']); assert.match(harness.elements.statusMsg.textContent, /copied/i); assert.equal(harness.elements.statusMsg.textContent.includes('sensitive-va-token'), false); }); test('clipboard permission denial is handled without leaking the token', async () => { const harness = makePopupHarness({ paired: true, confirmCopy: true, clipboardReject: new Error('NotAllowedError') }); await harness.controller.init(); await harness.controller.copyToken(); assert.match(harness.elements.statusMsg.textContent, /could not copy/i); assert.equal(harness.elements.statusMsg.textContent.includes('sensitive-va-token'), false); }); test('options UI stores a validated pairing secret locally and can forget pairing', () => { const html = read('options.html'); const js = read('options.js'); assert.match(html, /pairingSecret/); assert.match(html, /type="password"/); assert.doesNotMatch(html, /treat it like a password|privacy-note/i); assert.match(js, /chrome\.storage\.local\.set/); assert.match(js, /chrome\.storage\.local\.remove/); assert.match(js, /aptPairingSecret/); assert.doesNotMatch(js, /console\./); });