security(main): dialog-grant admission for local paths, explicit webPreferences

New localPathGrants.ts: an in-memory registry of paths the user picked in a
native dialog. Every check resolves realpath + stat at grant and use time;
Windows case folding; missing files, directories-as-files, devices and
symlinked parents can never pass. SFTP upload/download and zmodem send/
receive now refuse renderer-supplied local paths that were never granted,
returning the resolved path so callers never re-traverse a symlink. keyPath
is validated as a regular file <= 1MB before reading (a device node would
have blocked the UI thread forever). Tests inject a stub policy via
setLocalPathPolicy; production always defaults to the real registry.

Also: webPreferences now explicitly pins contextIsolation/nodeIntegration/
webSecurity instead of relying on defaults.

New offline test tests/local-path-grants.mjs (37 assertions incl. symlink
escape); offline suite grows to 13. i18n: 6 main.sftp/main.key error keys
in 4 languages.
This commit is contained in:
Bill committed 2026-10-07 22:06:45 +08:00
1 parent 9c75cdc7d4
commit d22923aedd
14 files changed
+612 -18

No files matched your search

+2
View File
@@ -26,6 +26,8 @@ const BUNDLES = [
// Known-hosts store: TOFU / changed / unreadable fail-closed behavior.
{ entry: 'src/main/knownHosts.ts', out: 'tests/.known-hosts.cjs' },
{ entry: 'src/main/settingsStore.ts', out: 'tests/.settings-store.cjs' },
// Local-path admission (grants): pure fs/path, no electron surface at all.
{ entry: 'src/main/localPathGrants.ts', out: 'tests/.local-path-grants.cjs' },
// Lock-password store: scrypt verifier, round trip, damaged-file handling.
{ entry: 'src/main/lockStore.ts', out: 'tests/.lock-store.cjs' },
// Lock controller: cooldown ladder, serialized attempts, persisted flags.