Repository navigation
fix(desktop): preference ownership, update-policy and fuse hardening from the 2026-10 security scan - #2138
Open
paustint wants to merge 4 commits into
Open
fix(desktop): preference ownership, update-policy and fuse hardening from the 2026-10 security scan#2138paustint wants to merge 4 commits into
paustint wants to merge 4 commits into
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Policy validation remains vulnerable to path races, and earlier auth-response branches can still log secrets.
3 open findings
What changed in this PR
Hardens desktop preference ownership, update policy validation, auth logging, and Electron fuses.
Changes:
- Moves folder selection and persistence into the main process.
- Restricts Windows policy files by owner SID and redacts validation logs.
- Disables Electron’s
runAsNodefuse.
| File | Description |
|---|---|
libs/desktop-types/src/lib/desktop-app.types.ts |
Updates folder-picker IPC types. |
electron-builder.config.js |
Disables runAsNode. |
apps/jetstream-desktop/src/services/persistence.service.ts |
Sanitizes renderer preferences. |
apps/jetstream-desktop/src/services/ipc.service.ts |
Adds main-process download picker. |
apps/jetstream-desktop/src/services/api.service.ts |
Redacts schema-mismatch logs. |
apps/jetstream-desktop/src/services/__tests__/persistence.service.spec.ts |
Tests preference ownership. |
apps/jetstream-desktop/src/preload.ts |
Exposes the replacement picker API. |
apps/jetstream-desktop/src/controllers/user.desktop.controller.ts |
Sanitizes profile preference updates. |
apps/jetstream-desktop/src/config/update-policy.ts |
Checks Windows policy ownership. |
apps/jetstream-desktop/src/config/__tests__/update-policy.spec.ts |
Tests trusted owner SIDs. |
apps/jetstream-desktop-client/src/app/components/settings/Settings.tsx |
Uses main-process folder selection. |
apps/docs/docs/getting-started/desktop-app-updates.mdx |
Documents Windows ownership requirements. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…y folders Both paths aim file I/O at a directory: history reads, writes and deletes go under one, every auto-saved download under the other. The IPC handler stripped dataHistoryFolder but not fileDownload.downloadPath, and the /api/me/profile desktop route stripped neither, so renderer code could point either at any directory. The folder dialog now runs and applies its choice in the main process (pickDownloadFolder replaces selectFolder), and every renderer entry point drops both fields.
paustint
force-pushed
the
sec/desktop-hardening
branch
from
October 8, 2026 19:09
5073775 to
fdf12e0
Compare
paustint
force-pushed
the
sec/desktop-hardening
branch
from
October 8, 2026 21:41
fdf12e0 to
d39e14f
Compare
paustint
force-pushed
the
sec/desktop-hardening
branch
from
October 10, 2026 15:06
d39e14f to
126f9bc
Compare
…ator owns it The default ACL on %ProgramData% lets a standard user create the Jetstream folder and policy.json, which marked every other user's install as managed and removed their update checks. The file now counts only when its owner SID is SYSTEM or Administrators; the macOS and Linux locations already require administrator rights to write.
…alidation A successful verify response carries the rotated session token and the org file encryption key; on a schema mismatch (server ahead of an un-updated desktop build) the whole payload went to main.log, which is less protected than the files it unlocks and is routinely attached to support emails. Only the payload keys and zod issues are logged now.
Nothing in the app re-executes itself as Node, and the fuse let any local process run arbitrary JavaScript under the signed, notarized Jetstream binary via ELECTRON_RUN_AS_NODE=1.
paustint
force-pushed
the
sec/desktop-hardening
branch
from
October 10, 2026 16:17
126f9bc to
53e6386
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Desktop-app fixes for 4 findings from the 2026-10-08 Claude Security scan (report in
CLAUDE-SECURITY-20261008-124758/, gitignored). Each finding was re-verified against the code first. One commit per finding.dataHistoryFolder(via the/api/me/profiledesktop route) andfileDownload.downloadPath(viasetPreferences), giving arbitrary file read/write/deletefix(desktop): stop the renderer from choosing the download and history folders%ProgramData%JSON file any local user can createfix(desktop): ignore a Windows update policy file unless an administrator owns it/desktop-app/auth/verifypayload (session JWT + org encryption key) logged on a zod mismatchfix(desktop): stop logging the auth payload when the response fails validationrunAsNodefuse enabledfix(desktop): disable the runAsNode Electron fuseBehavior changes worth a look
electronAPI.selectFolderis removed and replaced bypickDownloadFolder, which shows the folder dialog and persists the choice in the main process (same shape aspickDataHistoryFolder). Settings.tsx was the only caller. Main and renderer ship together, so no shim.setPreferencesIPC and the/api/me/profiledesktop route) now runs throughwithoutMainProcessOwnedPreferences, which dropsdataHistoryFolderand keeps the storedfileDownload.downloadPath. The one renderer-settable value is an empty download path, which only turns the save-as prompt back on ("Ask every time").C:\ProgramData\Jetstream\policy.jsoncreated by a standard user is now ignored (owner SID must beS-1-5-18SYSTEM orS-1-5-32-544Administrators, read viaGet-Acl). Files created by an elevated process or MDM keep working. Docs updated.ELECTRON_RUN_AS_NODE; the afterPack smoke test still strips it from the spawn env. A repo grep found no re-execution ofprocess.execPathas Node.Test plan
nx run-many -t typecheck -p jetstream-desktop jetstream-desktop-client desktop-typesnx run jetstream-desktop:test(6 files, 138 tests; 5 new preference-stripping cases, 2 new owner-SID cases)pnpm --dir apps/docs lintpolicy.jsonstill applies, user-created one is ignored (no Windows box here; the owner-SID decision is unit-tested, the PowerShell read is best-effort fail-closed)