Security and quality
Restrict navigation and new windows
Prevent the notes window from navigating away or creating an unexpected BrowserWindow.
Desktop Notes loads one bundled interface. It never needs to show a different page, so any navigation is by definition an attack or a bug.
The threat is concrete: if injected markup ever reaches the renderer — a crafted note, a templating slip — navigation is how it escalates. A window that navigates to an attacker’s page hands that page your preload bridge and your app’s identity. Renderer-created windows are the same problem through a second door: window.open() and target="_blank" spawn new pages you never planned for.
Desktop Notes does not need page navigation or renderer-created windows, so deny both:
win.webContents.on('will-navigate', event => {
event.preventDefault()
})
win.webContents.setWindowOpenHandler(() => ({
action: 'deny'
}))
will-navigate fires when the top frame is about to load a new URL — a clicked link, a form submission, or a location assignment. Calling event.preventDefault() cancels it. The window open handler runs before any new window exists, and returning { action: 'deny' } means one is never created. It does not fire for in-page anchor jumps, so nothing legitimate breaks.
Allowing one external link
If you later add an external documentation link, intercept it in the main process:
const { shell } = require('electron')
win.webContents.setWindowOpenHandler(({ url }) => {
const target = new URL(url)
if (
target.protocol === 'https:' &&
target.hostname === 'docs.desktop-notes.test'
) {
void shell.openExternal(target.href)
}
return { action: 'deny' }
})
The allowed link opens in the user’s browser, and the answer inside the app stays deny — always.
Parse the URL and compare exact protocol and hostname values. A startsWith() check can accept attacker-controlled hosts that merely begin with trusted text.
Never pass an unchecked string to shell.openExternal(). Protocol handlers can do more than open web pages.
Keep the restrictive content security policy in index.html. Avoid inline scripts because they require weakening script-src.
Verify the lockdown
Run the app, open DevTools, and try to escape:
window.open('https://attacker.invalid')
location.href = 'https://attacker.invalid'
Nothing should open, and the notes interface should stay put. Then test a normal click, window.open(), a link with target="_blank", and a form navigation. Include confusing URLs such as https://docs.desktop-notes.test.attacker.invalid and a non-HTTP protocol. Only the exact allowlisted link should leave the app — and it should leave through the system browser, never inside your window.
Lesson completed