fix: confirm proxy exit before disconnect
APT build checks / build-checks (push) Successful in 36s
APT build checks / build-checks (pull_request) Successful in 38s

This commit is contained in:
2026-08-19 23:12:56 +00:00
parent f65273c7a9
commit 4a10e6051e
5 changed files with 308 additions and 52 deletions
+61 -3
View File
@@ -275,13 +275,13 @@ test('disconnect stops every owned proxy before clearing the Alta session', asyn
},
},
});
const state = runtime.disconnect();
const state = await runtime.disconnect();
assert.deepEqual(stopped, [4101, 4102]);
assert.equal(state.connected, false);
assert.deepEqual(state.activeProxies, []);
});
test('disconnect reports failure truthfully and retains session when an owned proxy cannot stop', () => {
test('disconnect reports failure truthfully and retains session when an owned proxy cannot stop', async () => {
const sessionStore = createSessionStore();
sessionStore.establish('https://customer.avasecurity.com', 'synthetic-cookie');
const tracked = [{ processId: 4101, deviceId: '550e8400-e29b-41d4-a716-446655440000', status: 'running', startedAt: 1 }];
@@ -293,12 +293,52 @@ test('disconnect reports failure truthfully and retains session when an owned pr
stopProxy() { return { success: false, processId: 4101, status: 'permission-denied' }; },
},
});
const state = runtime.disconnect();
const state = await runtime.disconnect();
assert.equal(state.success, false);
assert.equal(state.connected, true);
assert.deepEqual(state.activeProxies.map((entry) => entry.processId), [4101]);
});
test('kill request without exit keeps proxy tracked and session connected until confirmed exit', async () => {
const { EventEmitter } = require('node:events');
const { createProxyManager } = require('../src/proxy-launch');
const sessionStore = createSessionStore();
sessionStore.establish('https://customer.avasecurity.com', 'synthetic-cookie');
const deviceId = '550e8400-e29b-41d4-a716-446655440000';
const child = new EventEmitter();
child.pid = 4101;
child.exitCode = null;
child.signalCode = null;
child.kill = () => true;
let timeoutCallback;
const proxyManager = createProxyManager({
appDirectory: 'C:\\Program Files\\Alta Proxy Tool',
fs: { existsSync: () => true },
spawn: () => child,
platform: 'win32',
stopTimeoutMs: 10,
setTimeout(callback) { timeoutCallback = callback; return 1; },
clearTimeout() {},
});
proxyManager.launchProxy({ deploymentHost: 'customer.avasecurity.com', username: 'operator@example.com', deviceId });
const runtime = new AppRuntime({ sessionStore, altaClient: {}, proxyManager });
const firstDisconnect = runtime.disconnect();
assert.equal(proxyManager.listTrackedProxies()[0].status, 'stopping');
timeoutCallback();
const failed = await firstDisconnect;
assert.equal(failed.success, false);
assert.equal(failed.connected, true);
assert.deepEqual(failed.activeProxies, [{ deviceId, processId: 4101 }]);
child.exitCode = 0;
child.emit('exit', 0, null);
const disconnected = await runtime.disconnect();
assert.equal(disconnected.success, true);
assert.equal(disconnected.connected, false);
assert.deepEqual(disconnected.activeProxies, []);
});
test('update runtime is check-only and opens only the fixed GitPeji releases page', async () => {
const opened = [];
const runtime = new AppRuntime({
@@ -340,6 +380,24 @@ test('preload and renderer expose only narrow, credential-free contracts', () =>
assert.match(html, /Bridge Pairing/);
});
test('Electron quit waits for confirmed cleanup and reports a safe failure instead of disposing', () => {
const main = read('main.js');
const beforeQuit = main.slice(main.indexOf("app.on('before-quit'"), main.indexOf("app.on('window-all-closed'"));
const preventIndex = beforeQuit.indexOf('event.preventDefault()');
const disconnectIndex = beforeQuit.indexOf('await runtime.disconnect()');
const activeCheckIndex = beforeQuit.indexOf('state.activeProxies.length > 0');
const disposeIndex = beforeQuit.indexOf('runtime.sessionStore.dispose()');
const confirmedQuitIndex = beforeQuit.indexOf('allowConfirmedQuit = true');
const quitIndex = beforeQuit.indexOf('app.quit()');
assert.ok(preventIndex >= 0 && preventIndex < disconnectIndex);
assert.ok(disconnectIndex < activeCheckIndex && activeCheckIndex < disposeIndex);
assert.ok(disposeIndex < confirmedQuitIndex && confirmedQuitIndex < quitIndex);
assert.match(main, /dialog\.showMessageBox/);
assert.match(main, /could not confirm that every proxy process exited/i);
assert.doesNotMatch(beforeQuit, /taskkill|pkill|wmic/i);
});
test('source policy removes legacy credential IPC, shell launch, broad kill, and executable updater', () => {
const production = ['main.js', 'preload.js', 'renderer.js', 'index.html', 'src/electron-runtime.js']
.map(read).join('\n');