1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 02:03:39 +00:00
Files
browser/apps/desktop/src/autofill/main/main-ssh-agent.service.ts
renovate[bot] 852248d5fa [deps] Platform: Update napi to v3 (major) (#16053)
* [deps] Platform: Update napi to v3

* fix: upgrade required dependencies

* fix: deprecated syntax in package.json

* fix: TS code after napi changes

* fix: lint

* fix: floating promise

* fix: libsqlite musl compilation

* feat: remove support for musl

* fix: sorting lint

* fix: logging not working

* fix: pre-emptive fix for passkey autofill

* fix: rust lint

* fix: package-lock

* fix: linux type error

* fix: windows type error

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com>
2025-12-10 09:43:51 -05:00

121 lines
3.8 KiB
TypeScript

// 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.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 | null, sshUiRequest: sshagent.SshUiRequest): Promise<boolean> => {
// 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);
}
});
}
}