fix(desktop): route updater manifest through OS trust and guard bare fetch - #3127
Conversation
…fetch The architecture-mismatch banner fetched the electron-updater manifest from github.com with bare Node fetch, so it only trusted bundled Mozilla roots and ignored system proxy configuration. On a TLS-inspecting corporate network that single call failed while the rest of the app worked, with no timeout to bound it. Route it through electronNet.fetch (Chromium: OS trust store + system proxy) with a 10s abort, keeping the warn-and-return-null failure behavior. apps/server/src already bans bare fetch through a test. Extend the same enforcement to the two remaining surfaces that talk to the network: - apps/desktop/electron: unmarked bare fetch fails; the four genuine loopback call sites (sidecar health, runtime JSON, daemon shutdown, CDP discovery) carry a // loopback-fetch: marker naming why 127.0.0.1 is provable. - apps/installer/src: bare fetch fails; external requests must use fetchWithSystemCa. system-ca.ts (defines the wrapper) and ui-html.ts (browser-side template) are exempt; Bun.serve's `async fetch(request)` handler is recognized as a definition, not a call. No configuration, API, or chart surface changes.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Daytona end-to-end validation — Linux complete, Windows build runningTested the actual PR in a clean Daytona Linux Electron sandbox (Electron 35.7.5), on a combined branch containing all six enterprise-network PRs. Real app / real changed functionFrom the live renderer through the preload bridge into the changed Electron main-process function: await window.__OPENWORK_ELECTRON__.system.getArchitectureInfo()Observed: {
"appArch":"x64",
"appArchLabel":"Intel",
"systemArch":"x64",
"systemArchLabel":"Intel",
"mismatch":false,
"platform":"linux",
"version":"0.0",
"downloadUrl":"https://github.com/different-ai/openwork/releases/latest/download/openwork-linux-x86_64-0.18.1.AppImage",
"releaseUrl":"https://github.com/different-ai/openwork/releases/latest"
}This is the exact changed path: Enterprise-CA differential on a real machineCreated a private CA + leaf for This reproduces the original bug exactly: the old bare-fetch code path rejects the enterprise CA even though the operating system trusts it. Important Linux boundary discoveredCalling the real Electron On this Debian image Chromium uses an NSS/user trust database rather than I corrected the new guard wording in commit Clean-box testsWindows proof in progressThe real repository Windows build workflow is building this combined branch now: I will use its Windows x64 artifact in a Daytona Windows VM with the fake corporate root installed in |
Daytona Windows E2E — PassedBuilt the exact combined branch containing this PR with the repository's real Build workflow (all five jobs passed, including Windows x64 + ARM64): EnvironmentMachine-store differentialInstalled the checked-in fake corporate root through {
"systemCount": 42,
"reproInSystem": 6,
"defaultCount": 150,
"reproInDefault": 0
}This is the exact enterprise/GPO case: Windows Real packaged app / real Electron main processLaunched the branch artifact in the interactive Administrator console session, connected through its real CDP target, then called the actual preload → IPC → Electron-main Healthy chain, signed by the corporate root: await window.__OPENWORK_ELECTRON__.invokeDesktop(
"__fetch",
"https://poc.openwork.test:8443/",
{ timeoutMs: 10000 }
)Observed: {"ok":true,"status":200,"body":"{\"workspaces\":[]}"}Broken chain (intermediate deliberately removed): {
"ok": false,
"error": "Error invoking remote method 'openwork:desktop': Error: net::ERR_CERT_AUTHORITY_INVALID"
}So the packaged app accepts the Windows machine-store corporate root, but still rejects an actually broken certificate chain. It is not bypassing verification. Actual changed function against production GitHubCalled the exact changed function through the real app: await window.__OPENWORK_ELECTRON__.system.getArchitectureInfo()Observed: {
"appArch":"x64",
"systemArch":"x64",
"mismatch":false,
"platform":"windows",
"version":"0.18.1",
"downloadUrl":"https://github.com/different-ai/openwork/releases/latest/download/openwork-win-x64-0.18.1.exe",
"releaseUrl":"https://github.com/different-ai/openwork/releases/latest"
}This proves ScreenshotVerdictPassed on Windows. The runtime change works on the platform where enterprise machine-store/GPO roots matter most, and verification remains strict for broken chains. The Linux boundary from the prior comment remains intentional and documented: Electron/Chromium may require NSS/user trust in addition to |

What broke
resolveCorrectArchitectureDownloadUrlinapps/desktop/electron/main.mjsfetched the electron-updater manifest fromgithub.comwith bare Nodefetch, twenty lines away from six correctelectronNet.fetchcalls in the same file.Bare
fetchonly trusts bundled Mozilla roots and ignores system proxy configuration. On a corporate network that inspects TLS, that one call fails while the whole rest of the app works — the hardest class of bug to diagnose. It also had no timeout.What this does
Fixes the leak. Routes it through
electronNet.fetch(Chromium stack: OS trust store + system proxy) with a 10s abort. Failure behavior is unchanged — still warns and returnsnull, so the banner can never block startup.Stops the next one.
apps/server/srcalready bans barefetchviano-bare-fetch.test.ts. This extends the same enforcement to the two remaining surfaces that talk to the network:apps/server/srcexternalFetch/loopbackFetchapps/desktop/electronelectronNet.fetchapps/installer/srcfetchWithSystemCaGenuine loopback call sites carry an explicit
// loopback-fetch: <reason>marker naming why 127.0.0.1 is provable, so the exemption is reviewable rather than invisible. Four in electron (sidecar health, runtime JSON, daemon shutdown, CDP discovery), one in the installer (its own local server).Compatibility
No breaking changes for self-hosted deployments. No configuration, environment variable, API, Helm chart, or database surface is touched. The only behavior change is that one manifest request now honors OS trust and system proxy — strictly more permissive than before, so networks that worked continue to work and inspected networks start working.
Tests run
```
pnpm --filter @openwork/desktop test # 117 tests, 116 pass, 0 fail, 1 skipped
cd apps/installer && bun test # 53 pass, 0 fail, 132 expect() calls
pnpm --filter @openwork/desktop typecheck:electron # pass
pnpm typecheck # pass
```
Negative controls — each guard was proven to actually catch the bug, in files this PR does not otherwise modify:
Reverting the fix in `main.mjs`:
```
Error: Unmarked bare fetch is banned in apps/desktop/electron because bare fetch
bypasses the OS certificate trust store and system proxy; use electronNet.fetch
for external requests, or add // loopback-fetch: if the target is
provably 127.0.0.1/localhost. Offenders:
main.mjs:300
```
Injecting `fetch("https://github.com/x")\` into `electron/updater.mjs` → caught at `updater.mjs:434`.
Injecting the same into `installer/src/config-sources.ts` → caught at `config-sources.ts:331`.
Adding a marked loopback fetch to the same file → passes, confirming the marker path works.
Bun.serve's `async fetch(request)` handler in `installer/src/server.ts` is correctly recognized as a definition, not a call.
Fraimz
Skipped, stated explicitly per AGENTS.md. This change has no user-visible surface: it is two CI guards plus one internal request that swaps its network stack. The observable effect only appears on a TLS-inspecting network, which is already covered end-to-end by the existing `installer-tls-trust` and `desktop-fetch-os-trust` flows. The executable proof for this PR is the negative controls above — each guard demonstrably fails on the exact bug it exists to prevent.