Adds @electron-forge/maker-deb so `electron-forge make` on Linux emits a
.deb next to the existing AppImage.
- forge.config: MakerDeb scoped to linux; `bin` pinned to packagerConfig.name
so the staging build ("Folo Staging") also resolves; folo:// and follow://
registered as x-scheme-handler MIME types; Network;News; categories.
- postMake: .deb is renamed to the Folo-<version>-linux-<arch>.deb convention
but kept out of latest-linux.yml (the electron-updater feed is AppImage-only).
- CI: install fakeroot/dpkg on the Linux leg; upload the .deb as its own
`linux-x64-deb` artifact; add *.deb to the attestation and release globs.
- pnpm-lock.yaml regenerated with the pinned pnpm@10.17.0. The large churn is
peer-key reserialization from a newer pnpm than last wrote the lockfile;
the only functional additions are maker-deb and its electron-installer-debian
subtree.
Claude-Session: https://claude.ai/code/session_01KeQJNfE1FfJnLMH9psG1jS
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
The 'integration.openURLScheme' IPC method invokes 'shell.openExternal'
with a renderer-supplied string after only checking that it contains
'://'. Electron's documentation explicitly warns that passing untrusted
URLs to 'shell.openExternal' is unsafe: schemes such as 'file://',
'smb://', 'ms-msdt:', 'search-ms:', 'jar:', 'res:', 'javascript:',
'data:' and 'vbscript:' have well-known abuse chains (local file
disclosure, NTLM credential theft over SMB on Windows, MSDT/Follina-style
RCE, etc.). Because the renderer process can also reach this IPC via any
XSS sink in untrusted RSS feed content, the previous validation was not
sufficient.
Replace the substring check with strict URL parsing plus an allowlist of
protocols that match the integration use-cases documented in the UI
(Obsidian, Bear, Drafts, Things, Notion, DEVONthink) plus generic
http/https/mailto. All other protocols are rejected with a clear error.
Adds vitest cases for representative dangerous schemes (verifying that
'shell.openExternal' is never invoked) and for every scheme shipped as a
built-in example, so future regressions on either side are caught.