AppForge-1 — install an app from a local folder or .brainstorm file (sideload path + packaged-path fix) - #364
Merged
Conversation
…torm file
- apps:install-from-folder / apps:install-from-file IPC: dashboard-sender-
gated (fail-closed), typed SideloadInstallResult, InstallOrigin.LocalFile
provenance, dashboard icon pinned like catalog installs
- Marketplace "Install from…" toolbar menu (shared fancy-menus picker) +
unsigned-advisory surfaced honestly (Local install source label, Unsigned
chip + Signature spec row; signatureStatus flows registry → listing wire)
- Hostile-input bounds: manifest size cap + fail-closed validator, archive
size cap before read, decompression-expansion cap via new
unpackBundle({maxOutputBytes}) (gzip-bomb defense), zip-slip guard
exercised from the install call site, mkdtemp staging removed in finally
- Packaged-path fix: marketplace first-party resolution via
process.resourcesPath/apps when app.isPackaged (was an asar-relative walk
that doesn't exist); reinstallFirstPartyApp prebuiltOnly never spawns
vite build in a packaged shell (installs the extraResources bundle with
bootstrap-cache provenance)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
AppForge-1 — install an app from a local folder or
.brainstormfileFirst real producer of the already-schema'd sideload path (
InstallOrigin.LocalFile): a user can now install an app bundle they have on disk, straight from the Marketplace. This is the install half of the "app built inside Brainstorm becomes a real app" loop.What's new
apps:install-from-folder/apps:install-from-fileIPC (main/ipc/sideload-handlers.ts): main-side OS dialog (openDirectory, andopenFilefiltered to*.brainstormper the dialog-filter rule) → manifest pre-check →AppInstaller.installwithInstallOrigin.LocalFileprovenance → dashboard icon pinned (mirrors the catalog install path). Results are always a typedSideloadInstallResult(installedwith app summary + granted caps |cancelled|failedwith aSideloadFailureCode) — the handler never throws raw errors at the renderer..brainstorm), success/failure toasts (typed-code → message map), and honest unsigned surfacing — a "Local install" source label, an Unsigned advisory chip + Signature spec row on the listing detail (signatureStatusnow flows registry row →MarketplaceService→ listing wire type).registerMarketplaceHandlersresolved first-party bundles via a__dirnamerelative walk that lands inside the asar in packaged builds — nowresolveFirstPartyAppsDirpicksprocess.resourcesPath/appswhenapp.isPackaged(same treebootstrapAppsuses).reinstallFirstPartyAppgainsprebuiltOnly(passed asapp.isPackaged): installs the prebuilt extraResources bundle withbootstrap-cacheprovenance and never spawnsvite buildin a packaged shell.Security posture (⚠️ flagging for security review — new IPC + install surface)
This is hostile-input handling; the following is the intended posture, please poke at it:
WebContents— any other sender gets a typednot-allowedfailure (fail-closed). App renderers cannot reach the installer.entry, malformed id, non-JSON, oversized.unpackBundle({ maxOutputBytes })(gzip-bomb defense, 256 MiB default); unpack goes to a freshmkdtempstaging dir removed infinally; the tar codec's absolute/..rejection +unpackBrainstormBundleToDir's per-entry containment re-check are both exercised from this call site (zip-slip test).AppSignatureStatus.Unsigned(advisory in v1,shouldBlockInstallremains the single enforcement chokepoint); trust quarantine (sideloaded action tier) applies as before. The unsigned state is surfaced to the user rather than hidden.Tests
main/ipc/sideload-handlers.test.ts(new, 13 cases): happy folder install (registry row provenance + icon pin + unsigned surfaced), cancel, hostile manifests (traversal entry / bad id / bad JSON / oversized), id collision, non-dashboard sender, no-session,.brainstormround-trip viapackBrainstormBundle, garbage bytes, zip-slip entry, over-limit file, decompression bomb.main/dev/seed-demo-apps.test.ts:prebuiltOnlyinstalls withbootstrap-cacheprovenance and a spawn spy provesvite buildis never spawned (including the missing-bundle failure path).main/apps/first-party.test.ts:resolveFirstPartyAppsDirdev + packaged branches.main/catalog/brainstorm-package.test.ts:maxOutputBytesbomb bound (tiny archive, big expansion → throws; within bound → unpacks).bun run verify,bun run lint,bun run typecheckgreen.🤖 Generated with Claude Code