Skip to content

Commit

Permalink
[PM-3683] Remove ipcRenderer from electron-platform-utils (#6679)
Browse files Browse the repository at this point in the history
* [PM-3683] Remove ipcRenderer from electron-platform-utils

* FIx review comments

* Formatting

* Use isNullOrWhitespace
  • Loading branch information
dani-garcia authored Nov 1, 2023
1 parent a1729c9 commit c592bcb
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 46 deletions.
4 changes: 2 additions & 2 deletions apps/desktop/src/auth/lock.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/pass
import { DialogService } from "@bitwarden/components";

import { ElectronStateService } from "../platform/services/electron-state.service.abstraction";
import { BiometricStorageAction, BiometricMessage } from "../types/biometric-message";
import { BiometricAction, BiometricMessage } from "../types/biometric-message";

const BroadcasterSubscriptionId = "LockComponent";

Expand Down Expand Up @@ -133,7 +133,7 @@ export class LockComponent extends BaseLockComponent {
private async canUseBiometric() {
const userId = await this.stateService.getUserId();
const val = await ipcRenderer.invoke("biometric", {
action: BiometricStorageAction.EnabledForUser,
action: BiometricAction.EnabledForUser,
key: `${userId}_user_biometric`,
keySuffix: KeySuffixOptions.Biometric,
userId: userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BiometricKey } from "@bitwarden/common/auth/types/biometric-key";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { passwords } from "@bitwarden/desktop-native";

import { BiometricMessage, BiometricStorageAction } from "../../types/biometric-message";
import { BiometricMessage, BiometricAction } from "../../types/biometric-message";

import { BiometricsServiceAbstraction } from "./biometric/index";

Expand Down Expand Up @@ -66,7 +66,7 @@ export class DesktopCredentialStorageListener {
}

switch (message.action) {
case BiometricStorageAction.EnabledForUser:
case BiometricAction.EnabledForUser:
if (!message.key || !message.userId) {
break;
}
Expand All @@ -76,7 +76,7 @@ export class DesktopCredentialStorageListener {
userId: message.userId,
});
break;
case BiometricStorageAction.OsSupported:
case BiometricAction.OsSupported:
val = await this.biometricService.osSupportsBiometric();
break;
default:
Expand Down
23 changes: 23 additions & 0 deletions apps/desktop/src/platform/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { ipcRenderer } from "electron";

import { DeviceType, ThemeType } from "@bitwarden/common/enums";

import { BiometricMessage, BiometricAction } from "../types/biometric-message";
import { isDev, isWindowsStore } from "../utils";

import { ClipboardWriteMessage } from "./types/clipboard";

const storage = {
get: <T>(key: string): Promise<T> => ipcRenderer.invoke("storageService", { action: "get", key }),
has: (key: string): Promise<boolean> =>
Expand All @@ -25,6 +28,22 @@ const passwords = {
ipcRenderer.invoke("keytar", { action: "deletePassword", key, keySuffix }),
};

const biometric = {
osSupported: (): Promise<boolean> =>
ipcRenderer.invoke("biometric", {
action: BiometricAction.OsSupported,
} satisfies BiometricMessage),
authenticate: (): Promise<boolean> =>
ipcRenderer.invoke("biometric", {
action: BiometricAction.Authenticate,
} satisfies BiometricMessage),
};

const clipboard = {
read: (): Promise<string> => ipcRenderer.invoke("clipboard.read"),
write: (message: ClipboardWriteMessage) => ipcRenderer.invoke("clipboard.write", message),
};

export default {
versions: {
app: (): Promise<string> => ipcRenderer.invoke("appVersion"),
Expand Down Expand Up @@ -52,8 +71,12 @@ export default {
});
},

launchUri: (uri: string) => ipcRenderer.invoke("launchUri", uri),

storage,
passwords,
biometric,
clipboard,
};

function deviceType(): DeviceType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { ipcRenderer, shell } from "electron";

import { ClientType, DeviceType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
Expand All @@ -8,7 +6,6 @@ import {
PlatformUtilsService,
} from "@bitwarden/common/platform/abstractions/platform-utils.service";

import { BiometricMessage, BiometricStorageAction } from "../../types/biometric-message";
import { isMacAppStore } from "../../utils";
import { ClipboardWriteMessage } from "../types/clipboard";

Expand Down Expand Up @@ -61,7 +58,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}

launchUri(uri: string, options?: any): void {
shell.openExternal(uri);
ipc.platform.launchUri(uri);
}

getApplicationVersion(): Promise<string> {
Expand Down Expand Up @@ -108,7 +105,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
const clearing = options?.clearing === true;
const clearMs = options?.clearMs ?? null;

ipcRenderer.invoke("clipboard.write", {
ipc.platform.clipboard.write({
text: text,
password: (options?.allowHistory ?? false) === false, // default to false
} satisfies ClipboardWriteMessage);
Expand All @@ -123,25 +120,19 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}

readFromClipboard(): Promise<string> {
return ipcRenderer.invoke("clipboard.read");
return ipc.platform.clipboard.read();
}

async supportsBiometric(): Promise<boolean> {
return await ipcRenderer.invoke("biometric", {
action: BiometricStorageAction.OsSupported,
} as BiometricMessage);
return await ipc.platform.biometric.osSupported();
}

/** This method is used to authenticate the user presence _only_.
* It should not be used in the process to retrieve
* biometric keys, which has a separate authentication mechanism.
* For biometric keys, invoke "keytar" with a biometric key suffix */
async authenticateBiometric(): Promise<boolean> {
const val = await ipcRenderer.invoke("biometric", {
action: "authenticate",
});

return val;
return await ipc.platform.biometric.authenticate();
}

supportsSecureStorage(): boolean {
Expand Down
19 changes: 18 additions & 1 deletion apps/desktop/src/services/electron-main-messaging.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import * as path from "path";

import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, session, Notification } from "electron";
import {
app,
dialog,
ipcMain,
Menu,
MenuItem,
nativeTheme,
session,
Notification,
shell,
} from "electron";

import { ThemeType } from "@bitwarden/common/enums";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { SafeUrls } from "@bitwarden/common/platform/misc/safe-urls";

import { WindowMain } from "../main/window.main";
import { RendererMenuItem } from "../utils";
Expand Down Expand Up @@ -68,6 +79,12 @@ export class ElectronMainMessagingService implements MessagingService {
alert.show();
});

ipcMain.handle("launchUri", async (event, uri) => {
if (SafeUrls.canLaunch(uri)) {
shell.openExternal(uri);
}
});

nativeTheme.on("updated", () => {
windowMain.win?.webContents.send(
"systemThemeUpdated",
Expand Down
5 changes: 3 additions & 2 deletions apps/desktop/src/types/biometric-message.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export enum BiometricStorageAction {
export enum BiometricAction {
EnabledForUser = "enabled",
OsSupported = "osSupported",
Authenticate = "authenticate",
}

export type BiometricMessage = {
action: BiometricStorageAction;
action: BiometricAction;
keySuffix?: string;
key?: string;
userId?: string;
Expand Down
21 changes: 21 additions & 0 deletions libs/common/src/platform/misc/safe-urls.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SafeUrls } from "./safe-urls";

describe("SafeUrls service", () => {
it("should allow valid URLs", () => {
expect(SafeUrls.canLaunch("https://bitwarden.com")).toBe(true);
expect(SafeUrls.canLaunch("http://bitwarden.com")).toBe(true);
expect(SafeUrls.canLaunch("ssh://my-server")).toBe(true);
});

it("should fail invalid URLs", () => {
expect(SafeUrls.canLaunch("bitwarden.com")).toBe(false);
expect(SafeUrls.canLaunch("")).toBe(false);
expect(SafeUrls.canLaunch(null)).toBe(false);
});

it("should fail URLs with disallowed protocols", () => {
expect(SafeUrls.canLaunch("file:///etc/passwd")).toBe(false);
expect(SafeUrls.canLaunch("\\\\network.share\\abc")).toBe(false);
expect(SafeUrls.canLaunch("smb://smb.server")).toBe(false);
});
});
33 changes: 33 additions & 0 deletions libs/common/src/platform/misc/safe-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Utils } from "./utils";

const CanLaunchWhitelist = [
"https://",
"http://",
"ssh://",
"ftp://",
"sftp://",
"irc://",
"vnc://",
// https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-uri
"rdp://", // Legacy RDP URI scheme
"ms-rd:", // Preferred RDP URI scheme
"chrome://",
"iosapp://",
"androidapp://",
];

export class SafeUrls {
static canLaunch(uri: string): boolean {
if (Utils.isNullOrWhitespace(uri)) {
return false;
}

for (let i = 0; i < CanLaunchWhitelist.length; i++) {
if (uri.indexOf(CanLaunchWhitelist[i]) === 0) {
return true;
}
}

return false;
}
}
28 changes: 4 additions & 24 deletions libs/common/src/vault/models/view/login-uri.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,10 @@ import { Jsonify } from "type-fest";

import { UriMatchType } from "../../../enums";
import { View } from "../../../models/view/view";
import { SafeUrls } from "../../../platform/misc/safe-urls";
import { Utils } from "../../../platform/misc/utils";
import { LoginUri } from "../domain/login-uri";

const CanLaunchWhitelist = [
"https://",
"http://",
"ssh://",
"ftp://",
"sftp://",
"irc://",
"vnc://",
// https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-uri
"rdp://", // Legacy RDP URI scheme
"ms-rd:", // Preferred RDP URI scheme
"chrome://",
"iosapp://",
"androidapp://",
];

export class LoginUriView implements View {
match: UriMatchType = null;

Expand Down Expand Up @@ -108,15 +93,10 @@ export class LoginUriView implements View {
return this._canLaunch;
}
if (this.uri != null && this.match !== UriMatchType.RegularExpression) {
const uri = this.launchUri;
for (let i = 0; i < CanLaunchWhitelist.length; i++) {
if (uri.indexOf(CanLaunchWhitelist[i]) === 0) {
this._canLaunch = true;
return this._canLaunch;
}
}
this._canLaunch = SafeUrls.canLaunch(this.launchUri);
} else {
this._canLaunch = false;
}
this._canLaunch = false;
return this._canLaunch;
}

Expand Down

0 comments on commit c592bcb

Please sign in to comment.