mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 18:23:31 +00:00
[PM-5796] Improve desktop biometric browser integration error handling (#7727)
* Re-register native messaging host integrations on startup * Check for errors when generating the manifests * Add log to component * Switch to Promise.all * Add injectable service
This commit is contained in:
@@ -22,7 +22,55 @@ export class NativeMessagingMain {
|
||||
private windowMain: WindowMain,
|
||||
private userPath: string,
|
||||
private exePath: string,
|
||||
) {}
|
||||
) {
|
||||
ipcMain.handle(
|
||||
"nativeMessaging.manifests",
|
||||
async (_event: any, options: { create: boolean }) => {
|
||||
if (options.create) {
|
||||
this.listen();
|
||||
try {
|
||||
await this.generateManifests();
|
||||
} catch (e) {
|
||||
this.logService.error("Error generating manifests: " + e);
|
||||
return e;
|
||||
}
|
||||
} else {
|
||||
this.stop();
|
||||
try {
|
||||
await this.removeManifests();
|
||||
} catch (e) {
|
||||
this.logService.error("Error removing manifests: " + e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
"nativeMessaging.ddgManifests",
|
||||
async (_event: any, options: { create: boolean }) => {
|
||||
if (options.create) {
|
||||
this.listen();
|
||||
try {
|
||||
await this.generateDdgManifests();
|
||||
} catch (e) {
|
||||
this.logService.error("Error generating duckduckgo manifests: " + e);
|
||||
return e;
|
||||
}
|
||||
} else {
|
||||
this.stop();
|
||||
try {
|
||||
await this.removeDdgManifests();
|
||||
} catch (e) {
|
||||
this.logService.error("Error removing duckduckgo manifests: " + e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
listen() {
|
||||
ipc.config.id = "bitwarden";
|
||||
@@ -76,7 +124,7 @@ export class NativeMessagingMain {
|
||||
ipc.server.emit(socket, "message", message);
|
||||
}
|
||||
|
||||
generateManifests() {
|
||||
async generateManifests() {
|
||||
const baseJson = {
|
||||
name: "com.8bit.bitwarden",
|
||||
description: "Bitwarden desktop <-> browser bridge",
|
||||
@@ -84,6 +132,10 @@ export class NativeMessagingMain {
|
||||
type: "stdio",
|
||||
};
|
||||
|
||||
if (!existsSync(baseJson.path)) {
|
||||
throw new Error(`Unable to find binary: ${baseJson.path}`);
|
||||
}
|
||||
|
||||
const firefoxJson = {
|
||||
...baseJson,
|
||||
...{ allowed_extensions: ["{446900e4-71c2-419f-a6a7-df9c091e268b}"] },
|
||||
@@ -92,8 +144,11 @@ export class NativeMessagingMain {
|
||||
...baseJson,
|
||||
...{
|
||||
allowed_origins: [
|
||||
// Chrome extension
|
||||
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
|
||||
// Edge extension
|
||||
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
|
||||
// Opera extension
|
||||
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
|
||||
],
|
||||
},
|
||||
@@ -102,27 +157,17 @@ export class NativeMessagingMain {
|
||||
switch (process.platform) {
|
||||
case "win32": {
|
||||
const destination = path.join(this.userPath, "browsers");
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.writeManifest(path.join(destination, "firefox.json"), firefoxJson);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
||||
await this.writeManifest(path.join(destination, "firefox.json"), firefoxJson);
|
||||
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.createWindowsRegistry(
|
||||
"HKLM\\SOFTWARE\\Mozilla\\Firefox",
|
||||
"HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
path.join(destination, "firefox.json"),
|
||||
);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.createWindowsRegistry(
|
||||
"HKCU\\SOFTWARE\\Google\\Chrome",
|
||||
"HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
path.join(destination, "chrome.json"),
|
||||
);
|
||||
const nmhs = this.getWindowsNMHS();
|
||||
for (const [key, value] of Object.entries(nmhs)) {
|
||||
let manifestPath = path.join(destination, "chrome.json");
|
||||
if (key === "Firefox") {
|
||||
manifestPath = path.join(destination, "firefox.json");
|
||||
}
|
||||
await this.createWindowsRegistry(value, manifestPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "darwin": {
|
||||
@@ -136,38 +181,30 @@ export class NativeMessagingMain {
|
||||
manifest = firefoxJson;
|
||||
}
|
||||
|
||||
this.writeManifest(p, manifest).catch((e) =>
|
||||
this.logService.error(`Error writing manifest for ${key}. ${e}`),
|
||||
);
|
||||
await this.writeManifest(p, manifest);
|
||||
} else {
|
||||
this.logService.warning(`${key} not found skipping.`);
|
||||
this.logService.warning(`${key} not found, skipping.`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "linux":
|
||||
if (existsSync(`${this.homedir()}/.mozilla/`)) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.writeManifest(
|
||||
await this.writeManifest(
|
||||
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
||||
firefoxJson,
|
||||
);
|
||||
}
|
||||
|
||||
if (existsSync(`${this.homedir()}/.config/google-chrome/`)) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.writeManifest(
|
||||
await this.writeManifest(
|
||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
chromeJson,
|
||||
);
|
||||
}
|
||||
|
||||
if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.writeManifest(
|
||||
await this.writeManifest(
|
||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
chromeJson,
|
||||
);
|
||||
@@ -178,20 +215,23 @@ export class NativeMessagingMain {
|
||||
}
|
||||
}
|
||||
|
||||
generateDdgManifests() {
|
||||
async generateDdgManifests() {
|
||||
const manifest = {
|
||||
name: "com.8bit.bitwarden",
|
||||
description: "Bitwarden desktop <-> DuckDuckGo bridge",
|
||||
path: this.binaryPath(),
|
||||
type: "stdio",
|
||||
};
|
||||
|
||||
if (!existsSync(manifest.path)) {
|
||||
throw new Error(`Unable to find binary: ${manifest.path}`);
|
||||
}
|
||||
|
||||
switch (process.platform) {
|
||||
case "darwin": {
|
||||
/* eslint-disable-next-line no-useless-escape */
|
||||
const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`;
|
||||
this.writeManifest(path, manifest).catch((e) =>
|
||||
this.logService.error(`Error writing manifest for DuckDuckGo. ${e}`),
|
||||
);
|
||||
await this.writeManifest(path, manifest);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -199,86 +239,50 @@ export class NativeMessagingMain {
|
||||
}
|
||||
}
|
||||
|
||||
removeManifests() {
|
||||
async removeManifests() {
|
||||
switch (process.platform) {
|
||||
case "win32":
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(path.join(this.userPath, "browsers", "firefox.json"));
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(path.join(this.userPath, "browsers", "chrome.json"));
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.deleteWindowsRegistry(
|
||||
"HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.deleteWindowsRegistry(
|
||||
"HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
);
|
||||
case "win32": {
|
||||
await this.removeIfExists(path.join(this.userPath, "browsers", "firefox.json"));
|
||||
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));
|
||||
|
||||
const nmhs = this.getWindowsNMHS();
|
||||
for (const [, value] of Object.entries(nmhs)) {
|
||||
await this.deleteWindowsRegistry(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "darwin": {
|
||||
const nmhs = this.getDarwinNMHS();
|
||||
for (const [, value] of Object.entries(nmhs)) {
|
||||
const p = path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json");
|
||||
if (existsSync(p)) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(p);
|
||||
}
|
||||
await this.removeIfExists(
|
||||
path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "linux":
|
||||
if (
|
||||
existsSync(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`)
|
||||
) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`);
|
||||
}
|
||||
|
||||
if (
|
||||
existsSync(
|
||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
)
|
||||
) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(
|
||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
existsSync(
|
||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
)
|
||||
) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(
|
||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
);
|
||||
}
|
||||
case "linux": {
|
||||
await this.removeIfExists(
|
||||
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
||||
);
|
||||
await this.removeIfExists(
|
||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
);
|
||||
await this.removeIfExists(
|
||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
removeDdgManifests() {
|
||||
async removeDdgManifests() {
|
||||
switch (process.platform) {
|
||||
case "darwin": {
|
||||
/* eslint-disable-next-line no-useless-escape */
|
||||
const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`;
|
||||
if (existsSync(path)) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fs.unlink(path);
|
||||
}
|
||||
await this.removeIfExists(path);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -286,6 +290,16 @@ export class NativeMessagingMain {
|
||||
}
|
||||
}
|
||||
|
||||
private getWindowsNMHS() {
|
||||
return {
|
||||
Firefox: "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
Chrome: "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
Chromium: "HKCU\\SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
// Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well.
|
||||
"Microsoft Edge": "HKCU\\SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
};
|
||||
}
|
||||
|
||||
private getDarwinNMHS() {
|
||||
/* eslint-disable no-useless-escape */
|
||||
return {
|
||||
@@ -305,10 +319,13 @@ export class NativeMessagingMain {
|
||||
}
|
||||
|
||||
private async writeManifest(destination: string, manifest: object) {
|
||||
this.logService.debug(`Writing manifest: ${destination}`);
|
||||
|
||||
if (!existsSync(path.dirname(destination))) {
|
||||
await fs.mkdir(path.dirname(destination));
|
||||
}
|
||||
fs.writeFile(destination, JSON.stringify(manifest, null, 2)).catch(this.logService.error);
|
||||
|
||||
await fs.writeFile(destination, JSON.stringify(manifest, null, 2));
|
||||
}
|
||||
|
||||
private binaryPath() {
|
||||
@@ -327,39 +344,26 @@ export class NativeMessagingMain {
|
||||
return regedit;
|
||||
}
|
||||
|
||||
private async createWindowsRegistry(check: string, location: string, jsonFile: string) {
|
||||
private async createWindowsRegistry(location: string, jsonFile: string) {
|
||||
const regedit = this.getRegeditInstance();
|
||||
|
||||
const list = util.promisify(regedit.list);
|
||||
const createKey = util.promisify(regedit.createKey);
|
||||
const putValue = util.promisify(regedit.putValue);
|
||||
|
||||
this.logService.debug(`Adding registry: ${location}`);
|
||||
|
||||
// Check installed
|
||||
try {
|
||||
await list(check);
|
||||
} catch {
|
||||
this.logService.warning(`Not finding registry ${check} skipping.`);
|
||||
return;
|
||||
}
|
||||
await createKey(location);
|
||||
|
||||
try {
|
||||
await createKey(location);
|
||||
// Insert path to manifest
|
||||
const obj: any = {};
|
||||
obj[location] = {
|
||||
default: {
|
||||
value: jsonFile,
|
||||
type: "REG_DEFAULT",
|
||||
},
|
||||
};
|
||||
|
||||
// Insert path to manifest
|
||||
const obj: any = {};
|
||||
obj[location] = {
|
||||
default: {
|
||||
value: jsonFile,
|
||||
type: "REG_DEFAULT",
|
||||
},
|
||||
};
|
||||
|
||||
return putValue(obj);
|
||||
} catch (error) {
|
||||
this.logService.error(error);
|
||||
}
|
||||
return putValue(obj);
|
||||
}
|
||||
|
||||
private async deleteWindowsRegistry(key: string) {
|
||||
@@ -385,4 +389,10 @@ export class NativeMessagingMain {
|
||||
return homedir();
|
||||
}
|
||||
}
|
||||
|
||||
private async removeIfExists(path: string) {
|
||||
if (existsSync(path)) {
|
||||
await fs.unlink(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user