Browse Source

[PM-3996] Scaffolding for preload script (#6065)

This PR wires up a polyfill for window.ipc which allows us to progressively migrate the codebase to a format which supports context bridge. This avoids a big bang effort where every non sandboxed call has to be migrated before we can run the code.

Once all calls to node modules are removed from the renderer and only exists in preload.ts. We will turn on context isolation and use the context bridge for communication instead.
pull/6561/head
Oscar Hinton 2 years ago committed by GitHub
parent
commit
7cfa38e344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/desktop/package.json
  2. 5
      apps/desktop/scripts/start.js
  3. 3
      apps/desktop/src/app/accounts/settings.component.ts
  4. 3
      apps/desktop/src/app/app.component.ts
  5. 4
      apps/desktop/src/app/main.ts
  6. 1
      apps/desktop/src/global.d.ts
  7. 1
      apps/desktop/src/main/window.main.ts
  8. 26
      apps/desktop/src/platform/preload.ts
  9. 27
      apps/desktop/src/platform/services/electron-platform-utils.service.ts
  10. 19
      apps/desktop/src/preload.ts
  11. 1
      apps/desktop/webpack.main.js
  12. 63
      apps/desktop/webpack.preload.js
  13. 2
      apps/desktop/webpack.renderer.js

4
apps/desktop/package.json

@ -19,8 +19,10 @@ @@ -19,8 +19,10 @@
"postinstall": "electron-rebuild",
"start": "cross-env ELECTRON_IS_DEV=0 ELECTRON_NO_UPDATER=1 electron ./build",
"build-native": "cd desktop_native && npm run build",
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
"build": "concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"",
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
"build:preload": "cross-env NODE_ENV=production webpack --config webpack.preload.js",
"build:preload:watch": "cross-env NODE_ENV=production webpack --config webpack.preload.js --watch",
"build:main": "cross-env NODE_ENV=production webpack --config webpack.main.js",
"build:main:dev": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.main.js",
"build:main:watch": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.main.js --watch",

5
apps/desktop/scripts/start.js

@ -13,6 +13,11 @@ concurrently( @@ -13,6 +13,11 @@ concurrently(
command: "npm run build:main:watch",
prefixColor: "yellow",
},
{
name: "Prel",
command: "npm run build:preload:watch",
prefixColor: "magenta",
},
{
name: "Rend",
command: "npm run build:renderer:watch",

3
apps/desktop/src/app/accounts/settings.component.ts

@ -21,7 +21,6 @@ import { DialogService } from "@bitwarden/components"; @@ -21,7 +21,6 @@ import { DialogService } from "@bitwarden/components";
import { flagEnabled } from "../../platform/flags";
import { ElectronStateService } from "../../platform/services/electron-state.service.abstraction";
import { isWindowsStore } from "../../utils";
import { SetPinComponent } from "../components/set-pin.component";
@Component({
selector: "app-settings",
@ -589,7 +588,7 @@ export class SettingsComponent implements OnInit { @@ -589,7 +588,7 @@ export class SettingsComponent implements OnInit {
this.form.controls.enableBrowserIntegration.setValue(false);
return;
} else if (isWindowsStore()) {
} else if (ipc.platform.isWindowsStore) {
await this.dialogService.openSimpleDialog({
title: { key: "browserIntegrationUnsupportedTitle" },
content: { key: "browserIntegrationWindowsStoreDesc" },

3
apps/desktop/src/app/app.component.ts

@ -10,7 +10,6 @@ import { @@ -10,7 +10,6 @@ import {
} from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router";
import { ipcRenderer } from "electron";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { firstValueFrom, Subject, takeUntil } from "rxjs";
@ -227,7 +226,7 @@ export class AppComponent implements OnInit, OnDestroy { @@ -227,7 +226,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.systemService.cancelProcessReload();
break;
case "reloadProcess":
ipcRenderer.send("reload-process");
ipc.platform.reloadProcess();
break;
case "syncStarted":
break;

4
apps/desktop/src/app/main.ts

@ -1,8 +1,12 @@ @@ -1,8 +1,12 @@
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { ipc } from "../preload";
import { isDev } from "../utils";
// Temporary polyfill for preload script
(window as any).ipc = ipc;
require("../scss/styles.scss");
require("../scss/tailwind.css");

1
apps/desktop/src/global.d.ts vendored

@ -1 +1,2 @@ @@ -1 +1,2 @@
declare module "forcefocus";
declare const ipc: typeof import("./preload").ipc;

1
apps/desktop/src/main/window.main.ts

@ -143,6 +143,7 @@ export class WindowMain { @@ -143,6 +143,7 @@ export class WindowMain {
backgroundColor: await this.getBackgroundColor(),
alwaysOnTop: this.enableAlwaysOnTop,
webPreferences: {
// preload: path.join(__dirname, "preload.js"),
spellcheck: false,
nodeIntegration: true,
backgroundThrottling: false,

26
apps/desktop/src/platform/preload.ts

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
import { ipcRenderer } from "electron";
import { DeviceType } from "@bitwarden/common/enums/device-type.enum";
import { isDev, isWindowsStore } from "../utils";
export default {
versions: {
app: (): Promise<string> => ipcRenderer.invoke("appVersion"),
},
deviceType: deviceType(),
isDev: isDev(),
isWindowsStore: isWindowsStore(),
reloadProcess: () => ipcRenderer.send("reload-process"),
};
function deviceType(): DeviceType {
switch (process.platform) {
case "win32":
return DeviceType.WindowsDesktop;
case "darwin":
return DeviceType.MacOsDesktop;
default:
return DeviceType.LinuxDesktop;
}
}

27
apps/desktop/src/platform/services/electron-platform-utils.service.ts

@ -9,31 +9,14 @@ import { @@ -9,31 +9,14 @@ import {
} from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { BiometricMessage, BiometricStorageAction } from "../../types/biometric-message";
import { isDev, isMacAppStore } from "../../utils";
import { isMacAppStore } from "../../utils";
import { ClipboardWriteMessage } from "../types/clipboard";
export class ElectronPlatformUtilsService implements PlatformUtilsService {
private deviceCache: DeviceType = null;
constructor(protected i18nService: I18nService, private messagingService: MessagingService) {}
getDevice(): DeviceType {
if (!this.deviceCache) {
switch (process.platform) {
case "win32":
this.deviceCache = DeviceType.WindowsDesktop;
break;
case "darwin":
this.deviceCache = DeviceType.MacOsDesktop;
break;
case "linux":
default:
this.deviceCache = DeviceType.LinuxDesktop;
break;
}
}
return this.deviceCache;
return ipc.platform.deviceType;
}
getDeviceString(): string {
@ -82,7 +65,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { @@ -82,7 +65,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}
getApplicationVersion(): Promise<string> {
return ipcRenderer.invoke("appVersion");
return ipc.platform.versions.app();
}
async getApplicationVersionNumber(): Promise<string> {
@ -92,7 +75,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { @@ -92,7 +75,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
// Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349
// has been merged and an updated electron build is available.
supportsWebAuthn(win: Window): boolean {
return process.platform === "win32";
return this.getDevice() === DeviceType.WindowsDesktop;
}
supportsDuo(): boolean {
@ -114,7 +97,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { @@ -114,7 +97,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}
isDev(): boolean {
return isDev();
return ipc.platform.isDev;
}
isSelfHost(): boolean {

19
apps/desktop/src/preload.ts

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
// import { contextBridge } from "electron";
import platform from "./platform/preload";
/**
* Bitwarden Preload script.
*
* This file contains the "glue" between the main process and the renderer process. Please ensure
* that you have read through the following articles before modifying any preload script.
*
* https://www.electronjs.org/docs/latest/tutorial/tutorial-preload
* https://www.electronjs.org/docs/latest/api/context-bridge
*/
// Each team owns a subspace of the `ipc` global variable in the renderer.
export const ipc = {
platform,
};
// contextBridge.exposeInMainWorld("ipc", ipc);

1
apps/desktop/webpack.main.js

@ -67,7 +67,6 @@ const main = { @@ -67,7 +67,6 @@ const main = {
],
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
"./src/package.json",

63
apps/desktop/webpack.preload.js

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
const path = require("path");
const { merge } = require("webpack-merge");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const configurator = require("./config/config");
const { EnvironmentPlugin } = require("webpack");
const NODE_ENV = process.env.NODE_ENV == null ? "development" : process.env.NODE_ENV;
console.log("Preload process config");
const envConfig = configurator.load(NODE_ENV);
configurator.log(envConfig);
const common = {
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules\/(?!(@bitwarden)\/).*/,
},
],
},
plugins: [],
resolve: {
extensions: [".tsx", ".ts", ".js"],
plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json" })],
},
};
const prod = {
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build"),
},
};
const dev = {
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build"),
devtoolModuleFilenameTemplate: "[absolute-resource-path]",
},
devtool: "cheap-source-map",
};
const main = {
mode: NODE_ENV,
target: "electron-preload",
node: {
__dirname: false,
__filename: false,
},
entry: {
preload: "./src/preload.ts",
},
optimization: {
minimize: false,
},
};
module.exports = merge(common, NODE_ENV === "development" ? dev : prod, main);

2
apps/desktop/webpack.renderer.js

@ -62,6 +62,8 @@ const common = { @@ -62,6 +62,8 @@ const common = {
const renderer = {
mode: NODE_ENV,
devtool: "source-map",
// TODO: Replace this with web.
// target: "web",
target: "electron-renderer",
node: {
__dirname: false,

Loading…
Cancel
Save