I was talking a lot about Electron security, and how just giving your frontend full access to your system might not be the best idea ever. So what else Electron recommends?
The current solution - and they reached that after a few iterations - is preload script.
We still have frontend and backend code, but we have third code - preload script - that initializes frontend, does any kind of highly privileged things, before handing it over to regular code.
If you do things right, frontend will have access to just the thing preload scripts setup, and nothing more. Which in our case won't help us at all, as we're writing terminal app, so the frontend is supposed to be able to literally any shell command, but this is great for more applications with more limited functionality, like a webchat app.
Enable preload script
Let's start with app we wrote in previous episode. We need te got rid of nodeIntegration: true
and contextIsolation: false
.
For some reason Electron really wants preload path to be absolute path, not relative path of file
URL:
let { app, BrowserWindow } = require("electron")
function createWindow() {
let win = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/preload.js`,
},
})
win.maximize()
win.loadFile("index.html")
}
app.on("ready", createWindow)
app.on("window-all-closed", () => {
app.quit()
})
Preload script
Let's move runCommand
to preload.js
, and use contextBridge
to expose it:
let child_process = require("child_process")
let { contextBridge } = require("electron")
let runCommand = (command) => {
return child_process.execSync(command).toString().trim()
}
contextBridge.exposeInMainWorld(
"api", { runCommand }
)
contextBridge.exposeInMainWorld
defines which extra things we expose in the frontend. In this case we told it that we want window.api
object, with a single method runCommand
.
It's important to note that this extra functionality will stay available even if you move to a completely different domain, so be careful.
Use preload script
In the frontend script, we just need to change two lines - remove the require
as it would no longer work, and call our exposed method window.api.runCommand
instead of child_process.execSync
:
form.addEventListener("submit", (e) => {
e.preventDefault()
let command = input.value
let output = window.api.runCommand(command)
createTerminalHistoryEntry(command, output)
input.value = ""
input.scrollIntoView()
})
Result
No screenshot this time, as it looks identical to previous episode, all that changes were internals.
So did we actually gain anything? Yes we did. As frontend script no longer uses weird require
shenanigans, it's now a regular frontend code, and we can use any bundler we want with it.
So over the next few episodes we'll be exploring how to use various frontend frameworks with Electron.
As usual, all the code for the episode is here.
Top comments (1)
I searched for more than 3 hours on how to enable a preload script as the normal 'path.join' function was giving a error and then i came across this article and this solutions here finally worked here thanks a lot very very helpful