1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-21 11:35:34 +01:00

[PM-3683] Remove ipcRenderer from electron-platform-utils (#6679)

* [PM-3683] Remove ipcRenderer from electron-platform-utils

* FIx review comments

* Formatting

* Use isNullOrWhitespace
This commit is contained in:
Daniel García 2023-11-01 18:34:36 +01:00 committed by GitHub
parent a1729c97df
commit c592bcba80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 46 deletions

View File

@ -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";
@ -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,

View File

@ -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";
@ -66,7 +66,7 @@ export class DesktopCredentialStorageListener {
}
switch (message.action) {
case BiometricStorageAction.EnabledForUser:
case BiometricAction.EnabledForUser:
if (!message.key || !message.userId) {
break;
}
@ -76,7 +76,7 @@ export class DesktopCredentialStorageListener {
userId: message.userId,
});
break;
case BiometricStorageAction.OsSupported:
case BiometricAction.OsSupported:
val = await this.biometricService.osSupportsBiometric();
break;
default:

View File

@ -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> =>
@ -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"),
@ -52,8 +71,12 @@ export default {
});
},
launchUri: (uri: string) => ipcRenderer.invoke("launchUri", uri),
storage,
passwords,
biometric,
clipboard,
};
function deviceType(): DeviceType {

View File

@ -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";
@ -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";
@ -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> {
@ -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);
@ -123,13 +120,11 @@ 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_.
@ -137,11 +132,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
* 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 {

View File

@ -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";
@ -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",

View File

@ -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;

View File

@ -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);
});
});

View File

@ -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;
}
}

View File

@ -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;
@ -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;
}