Fix main-process crash on SSH connection errors: handle repeated ssh2 error events, use the Windows OpenSSH agent only when it is running, show uncaught errors as a toast

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 22:51:33 +02:00
co-authored by Claude Opus 5.5
parent 7a9b70e645
commit 487ed069fc
3 changed files with 25 additions and 5 deletions
+1
View File
@@ -542,6 +542,7 @@
'A static configuration needs at least one IPv4 address (e.g. 192.168.1.10/24).': 'Eine statische Konfiguration braucht mindestens eine IPv4-Adresse (z. B. 192.168.1.10/24).', 'A static configuration needs at least one IPv4 address (e.g. 192.168.1.10/24).': 'Eine statische Konfiguration braucht mindestens eine IPv4-Adresse (z. B. 192.168.1.10/24).',
'A static IPv6 configuration needs at least one IPv6 address.': 'Eine statische IPv6-Konfiguration braucht mindestens eine IPv6-Adresse.', 'A static IPv6 configuration needs at least one IPv6 address.': 'Eine statische IPv6-Konfiguration braucht mindestens eine IPv6-Adresse.',
'Authentication failed for {user}. Check the username, password or key.': 'Anmeldung für {user} fehlgeschlagen. Prüfe Benutzername, Passwort oder Schlüssel.',
// Main-Prozess // Main-Prozess
'Host key has changed!': 'Host-Schlüssel hat sich geändert!', 'Host key has changed!': 'Host-Schlüssel hat sich geändert!',
'Unknown host': 'Unbekannter Host', 'Unknown host': 'Unbekannter Host',
+7
View File
@@ -32,6 +32,13 @@ if (process.platform === 'linux' && readLaunch().x11 !== false && !process.argv.
} }
const pendingSecrets = new Map(); const pendingSecrets = new Map();
// Sicherheitsnetz: unerwartete Fehler (z. B. aus Netzwerkbibliotheken) als Meldung anzeigen statt den Hauptprozess abstürzen zu lassen
process.on('uncaughtException', (e) => {
console.error('Uncaught exception:', e);
send('toast', e?.message || String(e), 'error');
});
process.on('unhandledRejection', (e) => console.error('Unhandled rejection:', e));
function send(channel, ...args) { function send(channel, ...args) {
if (win && !win.isDestroyed()) win.webContents.send(channel, ...args); if (win && !win.isDestroyed()) win.webContents.send(channel, ...args);
} }
+17 -5
View File
@@ -9,7 +9,11 @@ const i18n = require('../i18n');
function defaultAgent() { function defaultAgent() {
if (process.env.SSH_AUTH_SOCK) return process.env.SSH_AUTH_SOCK; if (process.env.SSH_AUTH_SOCK) return process.env.SSH_AUTH_SOCK;
if (process.platform === 'win32') return '\\\\.\\pipe\\openssh-ssh-agent'; if (process.platform === 'win32') {
// Nur verwenden, wenn der Windows-OpenSSH-Agent läuft (sonst schlägt die Agent-Anmeldung unnötig fehl)
const pipe = '\\\\.\\pipe\\openssh-ssh-agent';
try { return fs.existsSync(pipe) ? pipe : undefined; } catch { return undefined; }
}
return undefined; return undefined;
} }
@@ -111,8 +115,16 @@ class SshManager {
} }
finish(answers); finish(answers);
}); });
conn.once('ready', () => resolve(conn)); // ssh2 kann mehrere 'error'-Ereignisse senden (z. B. Anmeldefehler und danach Handshake-Timeout).
conn.once('error', async (err) => { // Ein dauerhafter Listener verhindert, dass ein späterer Fehler den Hauptprozess abstürzen lässt;
// Fehler nach dem Verbindungsaufbau melden die Sitzungen selbst (close/error).
let settled = false;
conn.on('error', () => {});
conn.once('ready', () => { settled = true; resolve(conn); });
conn.on('error', async (err) => {
if (settled) return;
settled = true;
try { conn.end(); } catch {}
// Kein Passwort hinterlegt und alle Methoden schlugen fehl -> nach Passwort fragen und neu versuchen // Kein Passwort hinterlegt und alle Methoden schlugen fehl -> nach Passwort fragen und neu versuchen
if (err.level === 'client-authentication' && !host.password && !host._retried) { if (err.level === 'client-authentication' && !host.password && !host._retried) {
const pw = await this.askSecret(sessionId, { title: i18n.t('Password'), prompt: i18n.t('Password for {user}', { user: `${cfg.username}@${host.address}` }), echo: false, host: host.label || host.address }); const pw = await this.askSecret(sessionId, { title: i18n.t('Password'), prompt: i18n.t('Password for {user}', { user: `${cfg.username}@${host.address}` }), echo: false, host: host.label || host.address });
@@ -121,9 +133,9 @@ class SshManager {
return; return;
} }
} }
reject(err); reject(err.level === 'client-authentication' ? new Error(i18n.t('Authentication failed for {user}. Check the username, password or key.', { user: `${cfg.username}@${host.address}` })) : err);
}); });
try { conn.connect(cfg); } catch (e) { reject(e); } try { conn.connect(cfg); } catch (e) { settled = true; reject(e); }
}); });
} }