You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(desktop): SSRF hardening + shared @sim/security/ssrf (re-home of #5763) (#5784)
* feat: re-home @sim/security/ssrf + sim SSRF dedup onto dev (clean core)
* feat(desktop): re-integrate SSRF guard + hardening onto rewritten dev
Re-applies the browser-agent SSRF guard and hardening onto dev's evolved
desktop files (dev rewrote session/driver/handoff/index and split out
errors.ts/keyboard.ts):
- session.ts: agent-partition onBeforeRequest is the SSRF choke point —
DNS-resolving check (fail-closed) for document navigations, synchronous
literal-IP backstop for subresources.
- driver.ts: browser_navigate/browser_open_tab validate via checkAgentUrl for a
clean model error; also adopt shared sleep/getErrorMessage and drop the local
reimplementations + banner separators.
- index.ts: local-only crashReporter (native minidumps, no upload) + CSP
fallback wired into the app session.
- window.ts: record the crash-dump dir on renderer_gone.
- config.ts: drop the local LOCAL_HOSTNAMES set for the shared isLoopbackHostname
(also removes the dead bare '::1').
- cdp.ts: per-WebContents callbacks so a background tab's events reach its own
driver.
- updater.ts: the manual check now surfaces network/manifest failures instead of
silently swallowing them.
- README: correct the App Sandbox / security-scoped-bookmark note.
- electron-mock: webRequest.onBeforeRequest + crashReporter stubs.
- api-validation: annotate dev's validated-envelope double-cast; bump the
route-count baseline 964→965 for dev's already-merged route (ratchets stay
tight; non-Zod and double-cast at baseline).
Skipped as moot (dev already did them independently): launcher isVisible removal,
decideStartRoute param drop, local-filesystem clear() removal.
* chore(desktop): biome format install-local.ts (pre-existing dev lint failure)
* refactor: apply audit cleanup (reuse + simplify)
- domain-check: drop the redundant isIpLiteral guard (isLoopbackIp already
validates and returns false for non-literals).
- session.ts: use shared getErrorMessage instead of the local error ternary
(the file already imports it).
- tray.ts: use shared sleep() instead of a hand-rolled setTimeout promise.
- updater.ts: distinguish the synchronous-throw log from the async-rejection
log on the manual update check.
* refactor: /simplify pass + review fixes
- url-guard: bound the SSRF dns.lookup with a 5s deadline (fails closed on
timeout) so a slow/hung resolver can't suspend the check and the
onBeforeRequest callback indefinitely (Greptile P2); + test.
- Finish the reuse consolidation the earlier pass missed: session.ts second
error ternary → getErrorMessage; the bracket-strip idiom → unwrapIpv6Brackets
in input-validation.ts, input-validation.server.ts (×2), onepassword/utils.ts
(fixes the check:utils banned-pattern CI failure).
- driver: document why the tool-level checkAgentUrl coexists with the
onBeforeRequest enforcement seam (clean model error; loadURL rejection is
swallowed).
* fix(desktop): swallow late DNS rejection after the SSRF lookup timeout (Cursor)
Copy file name to clipboardExpand all lines: apps/desktop/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -138,7 +138,7 @@ Good fits for the bridge: OS notifications + dock badge on workflow completion,
138
138
The main Copilot agent can inspect a user-selected local directory in the desktop app through request-local client tools (`local_mount_directory`, `local_list_mounts`, `local_list`, `local_glob`, `local_read`, `local_grep`, `local_stat`, and `local_forget_mount`). This capability is:
139
139
140
140
-**Explicit and read-only:** the native folder picker creates the grant; there are no write/delete/execute operations.
141
-
-**Remembered securely:** grants are encrypted in Electron's private app data with OS-backed `safeStorage` and restored with the same opaque URI after a normal app restart. Sandboxed macOS builds also retain the security-scoped bookmark. There is no plaintext fallback: when secure storage is unavailable, the returned mount has `remembered: false` and lasts only for that app session.
141
+
-**Remembered securely:** grants are encrypted in Electron's private app data with OS-backed `safeStorage` and restored with the same opaque URI after a normal app restart. (A security-scoped bookmark is stored alongside each grant, but it is a no-op in the current Developer ID build — only the macOS App Sandbox consumes it — and is kept purely for forward-compatibility should a sandboxed/MAS build ever ship.) There is no plaintext fallback: when secure storage is unavailable, the returned mount has `remembered: false` and lasts only for that app session.
142
142
-**Revocable:**`local_forget_mount` removes one grant. All grants are removed on explicit sign-out or server-origin change so another Sim account or server cannot inherit them. Normal app quit only releases active OS handles and keeps the encrypted grants.
143
143
-**Opaque:** renderer and model see only `localfs://<mount-id>/...` URIs. Electron resolves every URI, checks lexical and realpath containment, and refuses symlink escapes.
144
144
-**Desktop-only:** the web app advertises these request-local tools only when `window.simDesktop.localFilesystem` is present. They are available directly to the main agent and are not added to subagent allowlists.
0 commit comments