1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-22 11:13:46 +00:00

Move SSH Agent Files to Autofill Ownership (#13473)

* Move SSH agent files to Autofill ownership

* Fix ssh-agent.service.ts imports
This commit is contained in:
Colton Hurst
2025-03-04 12:31:36 -05:00
committed by GitHub
parent f7642aa0c6
commit 3bd60786b1
5 changed files with 5 additions and 6 deletions

View File

@@ -0,0 +1,130 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ipcMain } from "electron";
import { concatMap, delay, filter, firstValueFrom, from, race, take, timer } from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { sshagent } from "@bitwarden/desktop-napi";
class AgentResponse {
requestId: number;
accepted: boolean;
timestamp: Date;
}
export class MainSshAgentService {
SIGN_TIMEOUT = 60_000;
REQUEST_POLL_INTERVAL = 50;
private requestResponses: AgentResponse[] = [];
private request_id = 0;
private agentState: sshagent.SshAgentState;
constructor(
private logService: LogService,
private messagingService: MessagingService,
) {
ipcMain.handle(
"sshagent.importkey",
async (
event: any,
{ privateKey, password }: { privateKey: string; password?: string },
): Promise<sshagent.SshKeyImportResult> => {
return sshagent.importKey(privateKey, password);
},
);
ipcMain.handle("sshagent.init", async (event: any, message: any) => {
this.init();
});
ipcMain.handle("sshagent.isloaded", async (event: any) => {
return this.agentState != null;
});
}
init() {
// handle sign request passing to UI
sshagent
.serve(async (err: Error, sshUiRequest: sshagent.SshUiRequest) => {
// clear all old (> SIGN_TIMEOUT) requests
this.requestResponses = this.requestResponses.filter(
(response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT),
);
this.request_id += 1;
const id_for_this_request = this.request_id;
this.messagingService.send("sshagent.signrequest", {
cipherId: sshUiRequest.cipherId,
isListRequest: sshUiRequest.isList,
requestId: id_for_this_request,
processName: sshUiRequest.processName,
isAgentForwarding: sshUiRequest.isForwarding,
namespace: sshUiRequest.namespace,
});
const result = await firstValueFrom(
race(
from([false]).pipe(delay(this.SIGN_TIMEOUT)),
//poll for response
timer(0, this.REQUEST_POLL_INTERVAL).pipe(
concatMap(() => from(this.requestResponses)),
filter((response) => response.requestId == id_for_this_request),
take(1),
concatMap(() => from([true])),
),
),
);
if (!result) {
return false;
}
const response = this.requestResponses.find(
(response) => response.requestId == id_for_this_request,
);
this.requestResponses = this.requestResponses.filter(
(response) => response.requestId != id_for_this_request,
);
return response.accepted;
})
.then((agentState: sshagent.SshAgentState) => {
this.agentState = agentState;
this.logService.info("SSH agent started");
})
.catch((e) => {
this.logService.error("SSH agent encountered an error: ", e);
});
ipcMain.handle(
"sshagent.setkeys",
async (event: any, keys: { name: string; privateKey: string; cipherId: string }[]) => {
if (this.agentState != null && (await sshagent.isRunning(this.agentState))) {
sshagent.setKeys(this.agentState, keys);
}
},
);
ipcMain.handle(
"sshagent.signrequestresponse",
async (event: any, { requestId, accepted }: { requestId: number; accepted: boolean }) => {
this.requestResponses.push({ requestId, accepted, timestamp: new Date() });
},
);
ipcMain.handle("sshagent.lock", async (event: any) => {
if (this.agentState != null && (await sshagent.isRunning(this.agentState))) {
sshagent.lock(this.agentState);
}
});
ipcMain.handle("sshagent.clearkeys", async (event: any) => {
if (this.agentState != null) {
sshagent.clearKeys(this.agentState);
}
});
}
}