main: add context bridge communication example via preload script, set nodeIntegration to false by default, improve security

This commit is contained in:
leejooy96 committed 2023-02-01 09:38:23 +09:00
1 parent a540caf9d3
commit 8057a3521a
9 files changed
+64 -12

No files matched your search

+1
View File
@@ -22,6 +22,7 @@
│ │ │ └─ Constants.ts - Main global definition │ │ │ └─ Constants.ts - Main global definition
│ │ │ └─ Menus.ts - Main global menu definition │ │ │ └─ Menus.ts - Main global menu definition
│ │ └─ index.ts - Main process entry point │ │ └─ index.ts - Main process entry point
│ │ └─ IPCs.ts - Main process ipc handlers definition
│ │ └─ MainRunner.ts - Main process main window processing │ │ └─ MainRunner.ts - Main process main window processing
│ ├─ preload/ - Preload (Electron-Vue communication bridge) process source code │ ├─ preload/ - Preload (Electron-Vue communication bridge) process source code
│ │ └─ index.ts │ │ └─ index.ts
+14
View File
@@ -0,0 +1,14 @@
import { BrowserWindow, ipcMain } from 'electron'
import Constants from './utils/Constants'
/*
* IPC Communications
* */
export default class IPCs {
static initialize(window: BrowserWindow) {
// Get application version
ipcMain.on('msgRequestGetVersion', () => {
window.webContents.send('msgReceivedVersion', Constants.APP_VERSION)
})
}
}
+4
View File
@@ -1,6 +1,7 @@
import { app, BrowserWindow, RenderProcessGoneDetails } from 'electron' import { app, BrowserWindow, RenderProcessGoneDetails } from 'electron'
import Constants from './utils/Constants' import Constants from './utils/Constants'
import * as electronRemote from '@electron/remote/main' import * as electronRemote from '@electron/remote/main'
import IPCs from './IPCs'
const exitApp = (mainWindow: BrowserWindow): void => { const exitApp = (mainWindow: BrowserWindow): void => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
@@ -48,6 +49,9 @@ export const createMainWindow = async (mainWindow: BrowserWindow): Promise<Brows
await mainWindow.loadFile(Constants.APP_INDEX_URL_PROD) await mainWindow.loadFile(Constants.APP_INDEX_URL_PROD)
} }
// Initialize IPC Communication
IPCs.initialize(mainWindow)
return mainWindow return mainWindow
} }
+8 -4
View File
@@ -1,16 +1,20 @@
import { join } from 'path' import { join } from 'path'
import { name, version } from '../../../package.json'
export default class Constants { export default class Constants {
static APP_NAME = 'Vutron' // Display app name (uppercase first letter)
static APP_NAME = name.charAt(0).toUpperCase() + name.slice(1)
static APP_VERSION = version
static IS_DEV_ENV = process.env.NODE_ENV === 'development' static IS_DEV_ENV = process.env.NODE_ENV === 'development'
static IS_MAC = process.platform === 'darwin' static IS_MAC = process.platform === 'darwin'
static DEFAULT_WEB_PREFERENCES = { static DEFAULT_WEB_PREFERENCES = {
nodeIntegration: true, nodeIntegration: false,
contextIsolation: false, contextIsolation: true,
nodeIntegrationInWorker: true, enableRemoteModule: false,
preload: join(__dirname, '../preload/index.js') preload: join(__dirname, '../preload/index.js')
} }
+18 -4
View File
@@ -1,5 +1,19 @@
const main = (): void => { import { contextBridge, ipcRenderer, shell } from 'electron'
console.log('[Preload] Script loaded.')
}
main() // Whitelist of valid channels used for IPC communication (Send message from Renderer to Main)
const mainAvailChannels = ['msgRequestGetVersion']
const rendererAvailChannels = ['msgReceivedVersion']
contextBridge.exposeInMainWorld('mainApi', {
openExternal: async (url) => await shell.openExternal(url),
send: (channel, data) => {
if (mainAvailChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
},
receive: (channel, cbFunc) => {
if (rendererAvailChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) => cbFunc(...args))
}
}
})
+8
View File
@@ -6,6 +6,14 @@ import router from '@/renderer/router'
import vuetify from '@/renderer/plugins/vuetify' import vuetify from '@/renderer/plugins/vuetify'
import i18n from '@/renderer/plugins/i18n' import i18n from '@/renderer/plugins/i18n'
// Add API key defined in contextBridge to window object type
declare global {
// eslint-disable-next-line no-unused-vars
interface Window {
mainApi?: any
}
}
const app = createApp(App) const app = createApp(App)
app.use(vuetify).use(i18n).use(router).use(createPinia()) app.use(vuetify).use(i18n).use(router).use(createPinia())
+10
View File
@@ -11,9 +11,16 @@ const { counterIncrease } = useTempStore()
const { counter } = storeToRefs(useTempStore()) const { counter } = storeToRefs(useTempStore())
const theme = useTheme() const theme = useTheme()
const languages = ref(['en']) const languages = ref(['en'])
const appVersion = ref('Unknown')
onMounted((): void => { onMounted((): void => {
languages.value = availableLocales languages.value = availableLocales
// Get application version from package.json version string (Using IPC communication)
window.mainApi.receive('msgReceivedVersion', (version: string) => {
appVersion.value = version
})
window.mainApi.send('msgRequestGetVersion')
}) })
const handleChangeTheme = (): void => { const handleChangeTheme = (): void => {
@@ -51,6 +58,9 @@ const handleCountIncrease = (): void => {
<v-col cols="12" md="7"> <v-col cols="12" md="7">
<h2 class="my-4">{{ $t('desc.welcome-title') }}</h2> <h2 class="my-4">{{ $t('desc.welcome-title') }}</h2>
<p>{{ $t('desc.welcome-desc') }}</p> <p>{{ $t('desc.welcome-desc') }}</p>
<p class="my-4"
>App Version: <strong>{{ appVersion }}</strong></p
>
<v-row class="my-4"> <v-row class="my-4">
<v-col cols="3"> <v-col cols="3">
<v-btn icon color="primary" @click="handleChangeTheme"> <v-btn icon color="primary" @click="handleChangeTheme">
+1 -3
View File
@@ -1,12 +1,10 @@
import { shell } from 'electron'
export default class Utils { export default class Utils {
static getCurrentLocale(): string { static getCurrentLocale(): string {
return navigator?.language?.split('-')[0] || 'en' return navigator?.language?.split('-')[0] || 'en'
} }
static async openExternal(url: string): Promise<void> { static async openExternal(url: string): Promise<void> {
await shell.openExternal(url) await window.mainApi.openExternal(url)
} }
} }
-1
View File
@@ -68,7 +68,6 @@ export default defineConfig({
} }
]), ]),
rendererPlugin({ rendererPlugin({
nodeIntegration: true,
optimizeDeps: { optimizeDeps: {
include: ['path'] include: ['path']
} }