mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-02 18:17:46 +01: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:
parent
adb1ee3d38
commit
912b7c136e
@ -204,6 +204,8 @@ export class NativeMessagingBackground {
|
|||||||
this.privateKey = null;
|
this.privateKey = null;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
|
||||||
|
this.logService.error("NativeMessaging port disconnected because of error: " + error);
|
||||||
|
|
||||||
const reason = error != null ? "desktopIntegrationDisabled" : null;
|
const reason = error != null ? "desktopIntegrationDisabled" : null;
|
||||||
reject(new Error(reason));
|
reject(new Error(reason));
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@ import { DeviceType } from "@bitwarden/common/enums";
|
|||||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
@ -27,6 +28,7 @@ import { DialogService } from "@bitwarden/components";
|
|||||||
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
||||||
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
|
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
|
||||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||||
|
import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-settings",
|
selector: "app-settings",
|
||||||
@ -126,6 +128,8 @@ export class SettingsComponent implements OnInit {
|
|||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
|
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
|
||||||
private authRequestService: AuthRequestServiceAbstraction,
|
private authRequestService: AuthRequestServiceAbstraction,
|
||||||
|
private logService: LogService,
|
||||||
|
private nativeMessagingManifestService: NativeMessagingManifestService,
|
||||||
) {
|
) {
|
||||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||||
|
|
||||||
@ -628,11 +632,20 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.stateService.setEnableBrowserIntegration(this.form.value.enableBrowserIntegration);
|
await this.stateService.setEnableBrowserIntegration(this.form.value.enableBrowserIntegration);
|
||||||
this.messagingService.send(
|
|
||||||
this.form.value.enableBrowserIntegration
|
const errorResult = await this.nativeMessagingManifestService.generate(
|
||||||
? "enableBrowserIntegration"
|
this.form.value.enableBrowserIntegration,
|
||||||
: "disableBrowserIntegration",
|
|
||||||
);
|
);
|
||||||
|
if (errorResult !== null) {
|
||||||
|
this.logService.error("Error in browser integration: " + errorResult);
|
||||||
|
await this.dialogService.openSimpleDialog({
|
||||||
|
title: { key: "browserIntegrationErrorTitle" },
|
||||||
|
content: { key: "browserIntegrationErrorDesc" },
|
||||||
|
acceptButtonText: { key: "ok" },
|
||||||
|
cancelButtonText: null,
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.form.value.enableBrowserIntegration) {
|
if (!this.form.value.enableBrowserIntegration) {
|
||||||
this.form.controls.enableBrowserIntegrationFingerprint.setValue(false);
|
this.form.controls.enableBrowserIntegrationFingerprint.setValue(false);
|
||||||
@ -651,11 +664,19 @@ export class SettingsComponent implements OnInit {
|
|||||||
await this.stateService.setDuckDuckGoSharedKey(null);
|
await this.stateService.setDuckDuckGoSharedKey(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messagingService.send(
|
const errorResult = await this.nativeMessagingManifestService.generateDuckDuckGo(
|
||||||
this.form.value.enableDuckDuckGoBrowserIntegration
|
this.form.value.enableDuckDuckGoBrowserIntegration,
|
||||||
? "enableDuckDuckGoBrowserIntegration"
|
|
||||||
: "disableDuckDuckGoBrowserIntegration",
|
|
||||||
);
|
);
|
||||||
|
if (errorResult !== null) {
|
||||||
|
this.logService.error("Error in DDG browser integration: " + errorResult);
|
||||||
|
await this.dialogService.openSimpleDialog({
|
||||||
|
title: { key: "browserIntegrationUnsupportedTitle" },
|
||||||
|
content: errorResult.message,
|
||||||
|
acceptButtonText: { key: "ok" },
|
||||||
|
cancelButtonText: null,
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveBrowserIntegrationFingerprint() {
|
async saveBrowserIntegrationFingerprint() {
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NativeMessagingManifestService {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async generate(create: boolean): Promise<Error | null> {
|
||||||
|
return ipc.platform.nativeMessaging.manifests.generate(create);
|
||||||
|
}
|
||||||
|
async generateDuckDuckGo(create: boolean): Promise<Error | null> {
|
||||||
|
return ipc.platform.nativeMessaging.manifests.generateDuckDuckGo(create);
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,7 @@ import { SearchBarService } from "../layout/search/search-bar.service";
|
|||||||
|
|
||||||
import { DesktopFileDownloadService } from "./desktop-file-download.service";
|
import { DesktopFileDownloadService } from "./desktop-file-download.service";
|
||||||
import { InitService } from "./init.service";
|
import { InitService } from "./init.service";
|
||||||
|
import { NativeMessagingManifestService } from "./native-messaging-manifest.service";
|
||||||
import { RendererCryptoFunctionService } from "./renderer-crypto-function.service";
|
import { RendererCryptoFunctionService } from "./renderer-crypto-function.service";
|
||||||
|
|
||||||
const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
||||||
@ -249,6 +250,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: DesktopAutofillSettingsService,
|
provide: DesktopAutofillSettingsService,
|
||||||
deps: [StateProvider],
|
deps: [StateProvider],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: NativeMessagingManifestService,
|
||||||
|
useClass: NativeMessagingManifestService,
|
||||||
|
deps: [],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -1632,6 +1632,12 @@
|
|||||||
"browserIntegrationUnsupportedTitle": {
|
"browserIntegrationUnsupportedTitle": {
|
||||||
"message": "Browser integration not supported"
|
"message": "Browser integration not supported"
|
||||||
},
|
},
|
||||||
|
"browserIntegrationErrorTitle": {
|
||||||
|
"message": "Error enabling browser integration"
|
||||||
|
},
|
||||||
|
"browserIntegrationErrorDesc": {
|
||||||
|
"message": "An error has occurred while enabling browser integration."
|
||||||
|
},
|
||||||
"browserIntegrationMasOnlyDesc": {
|
"browserIntegrationMasOnlyDesc": {
|
||||||
"message": "Unfortunately browser integration is only supported in the Mac App Store version for now."
|
"message": "Unfortunately browser integration is only supported in the Mac App Store version for now."
|
||||||
},
|
},
|
||||||
|
@ -291,12 +291,20 @@ export class Main {
|
|||||||
this.powerMonitorMain.init();
|
this.powerMonitorMain.init();
|
||||||
await this.updaterMain.init();
|
await this.updaterMain.init();
|
||||||
|
|
||||||
if (
|
const [browserIntegrationEnabled, ddgIntegrationEnabled] = await Promise.all([
|
||||||
(await this.stateService.getEnableBrowserIntegration()) ||
|
this.stateService.getEnableBrowserIntegration(),
|
||||||
(await firstValueFrom(
|
firstValueFrom(this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$),
|
||||||
this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$,
|
]);
|
||||||
))
|
|
||||||
) {
|
if (browserIntegrationEnabled || ddgIntegrationEnabled) {
|
||||||
|
// Re-register the native messaging host integrations on startup, in case they are not present
|
||||||
|
if (browserIntegrationEnabled) {
|
||||||
|
this.nativeMessagingMain.generateManifests().catch(this.logService.error);
|
||||||
|
}
|
||||||
|
if (ddgIntegrationEnabled) {
|
||||||
|
this.nativeMessagingMain.generateDdgManifests().catch(this.logService.error);
|
||||||
|
}
|
||||||
|
|
||||||
this.nativeMessagingMain.listen();
|
this.nativeMessagingMain.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,22 +75,6 @@ export class MessagingMain {
|
|||||||
case "getWindowIsFocused":
|
case "getWindowIsFocused":
|
||||||
this.windowIsFocused();
|
this.windowIsFocused();
|
||||||
break;
|
break;
|
||||||
case "enableBrowserIntegration":
|
|
||||||
this.main.nativeMessagingMain.generateManifests();
|
|
||||||
this.main.nativeMessagingMain.listen();
|
|
||||||
break;
|
|
||||||
case "enableDuckDuckGoBrowserIntegration":
|
|
||||||
this.main.nativeMessagingMain.generateDdgManifests();
|
|
||||||
this.main.nativeMessagingMain.listen();
|
|
||||||
break;
|
|
||||||
case "disableBrowserIntegration":
|
|
||||||
this.main.nativeMessagingMain.removeManifests();
|
|
||||||
this.main.nativeMessagingMain.stop();
|
|
||||||
break;
|
|
||||||
case "disableDuckDuckGoBrowserIntegration":
|
|
||||||
this.main.nativeMessagingMain.removeDdgManifests();
|
|
||||||
this.main.nativeMessagingMain.stop();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,55 @@ export class NativeMessagingMain {
|
|||||||
private windowMain: WindowMain,
|
private windowMain: WindowMain,
|
||||||
private userPath: string,
|
private userPath: string,
|
||||||
private exePath: 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() {
|
listen() {
|
||||||
ipc.config.id = "bitwarden";
|
ipc.config.id = "bitwarden";
|
||||||
@ -76,7 +124,7 @@ export class NativeMessagingMain {
|
|||||||
ipc.server.emit(socket, "message", message);
|
ipc.server.emit(socket, "message", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateManifests() {
|
async generateManifests() {
|
||||||
const baseJson = {
|
const baseJson = {
|
||||||
name: "com.8bit.bitwarden",
|
name: "com.8bit.bitwarden",
|
||||||
description: "Bitwarden desktop <-> browser bridge",
|
description: "Bitwarden desktop <-> browser bridge",
|
||||||
@ -84,6 +132,10 @@ export class NativeMessagingMain {
|
|||||||
type: "stdio",
|
type: "stdio",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!existsSync(baseJson.path)) {
|
||||||
|
throw new Error(`Unable to find binary: ${baseJson.path}`);
|
||||||
|
}
|
||||||
|
|
||||||
const firefoxJson = {
|
const firefoxJson = {
|
||||||
...baseJson,
|
...baseJson,
|
||||||
...{ allowed_extensions: ["{446900e4-71c2-419f-a6a7-df9c091e268b}"] },
|
...{ allowed_extensions: ["{446900e4-71c2-419f-a6a7-df9c091e268b}"] },
|
||||||
@ -92,8 +144,11 @@ export class NativeMessagingMain {
|
|||||||
...baseJson,
|
...baseJson,
|
||||||
...{
|
...{
|
||||||
allowed_origins: [
|
allowed_origins: [
|
||||||
|
// Chrome extension
|
||||||
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
|
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
|
||||||
|
// Edge extension
|
||||||
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
|
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
|
||||||
|
// Opera extension
|
||||||
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
|
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -102,27 +157,17 @@ export class NativeMessagingMain {
|
|||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "win32": {
|
case "win32": {
|
||||||
const destination = path.join(this.userPath, "browsers");
|
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.
|
await this.writeManifest(path.join(destination, "firefox.json"), firefoxJson);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
||||||
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);
|
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
const nmhs = this.getWindowsNMHS();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
for (const [key, value] of Object.entries(nmhs)) {
|
||||||
this.createWindowsRegistry(
|
let manifestPath = path.join(destination, "chrome.json");
|
||||||
"HKLM\\SOFTWARE\\Mozilla\\Firefox",
|
if (key === "Firefox") {
|
||||||
"HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
manifestPath = path.join(destination, "firefox.json");
|
||||||
path.join(destination, "firefox.json"),
|
}
|
||||||
);
|
await this.createWindowsRegistry(value, manifestPath);
|
||||||
// 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"),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
@ -136,38 +181,30 @@ export class NativeMessagingMain {
|
|||||||
manifest = firefoxJson;
|
manifest = firefoxJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.writeManifest(p, manifest).catch((e) =>
|
await this.writeManifest(p, manifest);
|
||||||
this.logService.error(`Error writing manifest for ${key}. ${e}`),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
this.logService.warning(`${key} not found skipping.`);
|
this.logService.warning(`${key} not found, skipping.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "linux":
|
case "linux":
|
||||||
if (existsSync(`${this.homedir()}/.mozilla/`)) {
|
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.
|
await this.writeManifest(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.writeManifest(
|
|
||||||
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
||||||
firefoxJson,
|
firefoxJson,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsSync(`${this.homedir()}/.config/google-chrome/`)) {
|
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.
|
await this.writeManifest(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.writeManifest(
|
|
||||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||||
chromeJson,
|
chromeJson,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) {
|
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.
|
await this.writeManifest(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.writeManifest(
|
|
||||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
||||||
chromeJson,
|
chromeJson,
|
||||||
);
|
);
|
||||||
@ -178,20 +215,23 @@ export class NativeMessagingMain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateDdgManifests() {
|
async generateDdgManifests() {
|
||||||
const manifest = {
|
const manifest = {
|
||||||
name: "com.8bit.bitwarden",
|
name: "com.8bit.bitwarden",
|
||||||
description: "Bitwarden desktop <-> DuckDuckGo bridge",
|
description: "Bitwarden desktop <-> DuckDuckGo bridge",
|
||||||
path: this.binaryPath(),
|
path: this.binaryPath(),
|
||||||
type: "stdio",
|
type: "stdio",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!existsSync(manifest.path)) {
|
||||||
|
throw new Error(`Unable to find binary: ${manifest.path}`);
|
||||||
|
}
|
||||||
|
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
/* eslint-disable-next-line no-useless-escape */
|
/* 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`;
|
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) =>
|
await this.writeManifest(path, manifest);
|
||||||
this.logService.error(`Error writing manifest for DuckDuckGo. ${e}`),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -199,86 +239,50 @@ export class NativeMessagingMain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeManifests() {
|
async removeManifests() {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "win32":
|
case "win32": {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.removeIfExists(path.join(this.userPath, "browsers", "firefox.json"));
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));
|
||||||
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.
|
const nmhs = this.getWindowsNMHS();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
for (const [, value] of Object.entries(nmhs)) {
|
||||||
fs.unlink(path.join(this.userPath, "browsers", "chrome.json"));
|
await this.deleteWindowsRegistry(value);
|
||||||
// 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",
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
const nmhs = this.getDarwinNMHS();
|
const nmhs = this.getDarwinNMHS();
|
||||||
for (const [, value] of Object.entries(nmhs)) {
|
for (const [, value] of Object.entries(nmhs)) {
|
||||||
const p = path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json");
|
await this.removeIfExists(
|
||||||
if (existsSync(p)) {
|
path.join(value, "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(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDdgManifests() {
|
async removeDdgManifests() {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
/* eslint-disable-next-line no-useless-escape */
|
/* 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`;
|
const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`;
|
||||||
if (existsSync(path)) {
|
await this.removeIfExists(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);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
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() {
|
private getDarwinNMHS() {
|
||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
return {
|
return {
|
||||||
@ -305,10 +319,13 @@ export class NativeMessagingMain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async writeManifest(destination: string, manifest: object) {
|
private async writeManifest(destination: string, manifest: object) {
|
||||||
|
this.logService.debug(`Writing manifest: ${destination}`);
|
||||||
|
|
||||||
if (!existsSync(path.dirname(destination))) {
|
if (!existsSync(path.dirname(destination))) {
|
||||||
await fs.mkdir(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() {
|
private binaryPath() {
|
||||||
@ -327,24 +344,14 @@ export class NativeMessagingMain {
|
|||||||
return regedit;
|
return regedit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createWindowsRegistry(check: string, location: string, jsonFile: string) {
|
private async createWindowsRegistry(location: string, jsonFile: string) {
|
||||||
const regedit = this.getRegeditInstance();
|
const regedit = this.getRegeditInstance();
|
||||||
|
|
||||||
const list = util.promisify(regedit.list);
|
|
||||||
const createKey = util.promisify(regedit.createKey);
|
const createKey = util.promisify(regedit.createKey);
|
||||||
const putValue = util.promisify(regedit.putValue);
|
const putValue = util.promisify(regedit.putValue);
|
||||||
|
|
||||||
this.logService.debug(`Adding registry: ${location}`);
|
this.logService.debug(`Adding registry: ${location}`);
|
||||||
|
|
||||||
// Check installed
|
|
||||||
try {
|
|
||||||
await list(check);
|
|
||||||
} catch {
|
|
||||||
this.logService.warning(`Not finding registry ${check} skipping.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await createKey(location);
|
await createKey(location);
|
||||||
|
|
||||||
// Insert path to manifest
|
// Insert path to manifest
|
||||||
@ -357,9 +364,6 @@ export class NativeMessagingMain {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return putValue(obj);
|
return putValue(obj);
|
||||||
} catch (error) {
|
|
||||||
this.logService.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteWindowsRegistry(key: string) {
|
private async deleteWindowsRegistry(key: string) {
|
||||||
@ -385,4 +389,10 @@ export class NativeMessagingMain {
|
|||||||
return homedir();
|
return homedir();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async removeIfExists(path: string) {
|
||||||
|
if (existsSync(path)) {
|
||||||
|
await fs.unlink(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,13 @@ const nativeMessaging = {
|
|||||||
onMessage: (callback: (message: LegacyMessageWrapper | Message) => void) => {
|
onMessage: (callback: (message: LegacyMessageWrapper | Message) => void) => {
|
||||||
ipcRenderer.on("nativeMessaging", (_event, message) => callback(message));
|
ipcRenderer.on("nativeMessaging", (_event, message) => callback(message));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
manifests: {
|
||||||
|
generate: (create: boolean): Promise<Error | null> =>
|
||||||
|
ipcRenderer.invoke("nativeMessaging.manifests", { create }),
|
||||||
|
generateDuckDuckGo: (create: boolean): Promise<Error | null> =>
|
||||||
|
ipcRenderer.invoke("nativeMessaging.ddgManifests", { create }),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const crypto = {
|
const crypto = {
|
||||||
|
Loading…
Reference in New Issue
Block a user