Dependency security design
Scope: npm manifests and lockfiles, with a shared record for the parallel Python dependency workstream. The source and installed-App boundaries are recorded separately.
Current implementation and verified gap
The repository uses npm workspaces for apps/web, apps/desktop, and apps/cli. The Web application is an App Router application with output: "export"; its generated files are served by the Python worker. The desktop shell uses Electron and electron-builder. The root lockfile contains runtime, build, and optional dependency graphs for all workspaces.
The canonical Dependabot inventory at /tmp/openprogram-completion-audit/dependabot-open.json records 194 open alerts at the base: 154 npm alerts across 18 package names and 40 pip alerts across uv.lock. The npm baseline has 6 critical, 54 high, 78 medium, and 16 low alerts; 55 records are direct and 99 are transitive. The ignored report reports/content-security/npm-dependency-security-baseline.md records the exact advisories, ranges, versions, and commands. Range reconciliation treats comma-separated GitHub ranges as semver conjunctions and fails on parse errors.
Reference corpus and framework comparison
Fixed versions come from the GitHub advisory records and package registries. Registry metadata is checked with npm view before changing the lockfile. The selected framework versions require Node 22.12 or newer, so the root engine contract, CI bootstrap, and developer setup must use the same minimum.
| Area | Existing capability | Absent capability or risk | Decision |
|---|---|---|---|
| Next.js | App Router, static export, client-side runtime APIs, no Next server in the packaged path. | The installed 14.2.35 matched the affected ranges for current Next advisories, and Next 16 removes next lint. | Use Next 16.3.5 with eslint 9 and a direct lint script. Keep static export through dynamic = "force-static" for the manifest route and the existing webpack build. |
| Electron | Native shell, preload isolation, packaged runtime, worker URL on loopback. | Electron 37.10.3 matched the affected lock/direct records. | Use Electron 44.4.3. Its package and unpacked app declare macOS 13 as the minimum; desktop checks cover the source API surface. Packaging must replace the installed Electron bundle before local App acceptance. |
| Raster annotations | Image crop, rotate, draw, text, shape, save, undo, redo, and selection/drag editing. | tui-image-editor carried the vulnerable Fabric 4 chain and the replacement must preserve annotation interaction. | Remove tui-image-editor and use maintained Fabric 7.4.0 directly as a transparent annotation layer over the native canvas. Crop and rotate flatten annotations into the image; selection, movement, resize, and text editing remain available before export. |
| Transitive npm graph | One npm lockfile pins reproducible workspace dependencies. | Old nested tar, form-data, postcss, extract-zip, and legacy HTTP packages remained in the graph. | Refresh compatible transitive paths and remove unmaintained chains where the graph has no fixed release. The final lock is checked against every advisory range, including package-absent records. |
| Python graph | uv.lock is independently managed by the Python dependency workstream. | AnyIO, torch, setuptools, and other pip advisories must be reconciled with their exact fixed ranges. | Keep the same inventory, evidence, and full-gate contract; the parallel owner supplies Python version changes and evidence before integration. |
Official references: Next.js 16 upgrade guide, Next.js 16.3.5 release, Electron 44 release, Fabric advisory GHSA-hfvx-25r5-qc3w, Fabric advisory GHSA-w22m-hvvm-xmwx, and the per-advisory links recorded in the ignored baseline and reconciliation reports.
Adopted, modified, and rejected choices
- Adopt the advisory's first patched version or a later compatible stable release. Verify the resulting installed graph with both
npm audit --jsonand the canonical inventory ranges. - Modify the direct framework upgrade to preserve the static export. Web and root React remain on the existing React 18 contract; the CLI keeps its existing React 19 contract. Only the Next compatibility files and Node engine contract change.
- Reject
npm audit fix --forceas an implementation method because its result can choose unrelated major versions without compatibility evidence. - Reject dismissing alerts because the Web page is statically exported or a dependency is marked development-only. The complete declared graph is audited; applicability is recorded as evidence rather than used to suppress upgrades.
- For
extract-zipand legacyrequest, the inventory has no usable fixed release for some records. Trace every parent, remove the affected chain where possible, and record any remaining package-absent record with its parent chain and tested alternative.
Target architecture and boundaries
All package resolution goes through the root npm lockfile. Direct workspace manifests declare the supported framework and shell versions; nested package entries are accepted only when their parent package requires them. The build boundary is npm ci --ignore-scripts followed by workspace checks. The runtime boundary is the exported Web assets, Electron main/preload code, and the Python worker that serves those assets. Untrusted input must not be passed to package CLIs or archive extractors without the existing product validation.
Electron package resolution does not update an existing /Applications/OpenProgram.app by itself. The supported local path is to package the current checkout with the trusted runtime base, install the resulting app over the single default App, then run scripts/refresh-local-app.sh once. Verification must inspect the installed app's actual process.versions.electron; source-only checks do not establish the installed runtime version.
The Python dependency workstream owns uv.lock and records its host policy in the shared report: Intel macOS supports BM25, the optional embedding wheel is unavailable on Intel, and macOS arm64 embedding wheels require Python 3.14 or newer. The current verification host is macOS 15.7.3.
Security failures are handled as dependency-resolution failures: a clean install, audit, typecheck, build, or affected test failure blocks completion and is recorded with the exact command. Compatibility failures preserve the old manifest in Git until the minimum source adjustment is proven by the affected check. No remote write, release, installed-App refresh, or Python source edit is in this workstream.
Acceptance criteria
- At the final commit, every direct npm manifest version and every lockfile instance is outside each applicable fixed advisory range in the canonical inventory; no alert is dismissed or hidden.
npm ci --ignore-scriptssucceeds on the supported Node/npm engines,npm audit --jsonreports zero vulnerabilities, and the canonical range reconciliation reports no affected npm version. If an upstream no-patch advisory remains, the report names its parent chain, reason, and tested replacement.npm run build --workspace apps/web,npm run typecheck --workspace apps/cli,npm test --workspace apps/web,npm run check:web,npm run check:desktop, andnpm run check:cliare run from a clean install. The final report distinguishes existing source failures from dependency-induced failures.- The final diff contains only dependency manifests/lockfile, necessary Next/Electron compatibility source, the canonical design, and the ignored evidence rule. The worktree is clean after the scoped commit.
Full gate manifest
npm ci --ignore-scriptsnpm audit --jsonand the canonical reconciliation script over the final lockfilenpm run build --workspace apps/webnpm run typecheck --workspace apps/cliandnpm test --workspace apps/clinpm test --workspace apps/webandnpm run check:webnpm run check:desktopgit diff --check, manifest/lockfile consistency, and clean-worktree verification
Implementation status
The npm candidate uses Next 16.3.5, Electron 44.4.3, Fabric 7.4.0, and the fixed transitive graph recorded in the lockfile. Source checks cover static export, direct lint invocation, the native desktop surface, and the raster editor's crop, rotate, draw, annotation, save, undo, redo, and destroy paths. The Python section remains a coordination boundary for the separate uv.lock changes. Final audit counts, reconciliation output, and residual source-test failures belong in the ignored final ledger.