# Dependency security and npm maintenance

## Scope and decisions

The governance change in PR #5 kept `@onedotmint/reroute` at **0.4.0**, as one CLI + Pi extension package, without publishing it. The separately authorized [0.4.1 patch](RELEASE-0.4.1.md) prepares these fixes for npm distribution. Neither change alters credentials/configuration formats or authorizes cloud work. All cloud compatibility evidence for this patch is offline contract/type checking, not a new paid/live-service qualification.

- **Repository:** `package-lock.json` v3, committed; development and CI use `npm ci --ignore-scripts`. No floating CI dependency resolution for Reroute's own tree.
- **Publication:** no `npm-shrinkwrap.json` and no package lock in the tarball. npm 12 no longer honors shrinkwrap, including in dependency tarballs. Removing it is not a security fix by itself: the manifest must also admit safe production dependencies.
- **Production:** keep all six runtime dependencies. Daytona, E2B and Modal remain installed, not optional, so selecting a supported provider does not fail because its SDK was omitted. No new dependency, lifecycle script, or root override is introduced.
- **Version policy:** Daytona `~0.223.1`, Modal `~0.11.0` and E2B `~2.60.0` admit patch releases, not unreviewed SDK minor changes. The small stable libraries use `^` within their current major. Development tools/host SDKs remain pinned in the manifest and lock.
- **Pi:** both directly imported SDKs (`pi-coding-agent`, `pi-ai`) are explicit development dependencies and optional `*` peers. The native TypeScript/jiti loader maps them to the **host's** modules. The compiled CLI entry never needs these peers. A plain consumer install must not pull Pi, GenAI, esbuild or TypeScript. The real independently installed host test proves this mapping without a repository/global module fallback.

An upstream release does **not** upgrade an existing installation. Dependabot changes the repository manifest/lock in a PR; CI uses that exact lock. Consumers obtain allowed versions through their ordinary install/update workflows and may have their own lock. Existing installations need an update/reinstall after a future Reroute release; this PR cannot repair already-published 0.4.0 tarballs.

## Audit investigation

Public registry/advisory observations on **2026-10-31 UTC**. npm initially reported **7 findings (5 moderate, 2 high)** in the full original tree; `--omit=dev` reported **5 (3 moderate, 2 high)**. A later saved baseline full report counted 6 as registry metavulnerability data changed. These are package/metavulnerability counts, not seven independent exploitable bugs. `.artifacts/audit-*.json` retain command exit codes and complete advisory details in CI; advisory results can change without a code change.

| Original path / dependency | Classification and relevance | Change / remaining condition |
|---|---|---|
| Daytona 0.223.1 → Axios 1.20.0 → follow-redirects 1.16.1 | Indirect production, high: [GHSA-mrwp-f3q8-3g9p](https://github.com/advisories/GHSA-mrwp-f3q8-3g9p), cross-realm custom sensitive-header handling. The advisory did **not** reproduce its exploit through Axios 1.20.0; do not claim demonstrated Reroute credential leakage. | Refresh within Axios's existing `^1.16.0` to **1.16.2**. No Daytona upgrade required. |
| E2B 2.55.0 → Undici 7.30.0 / optional alias `undici8` 8.11.2 | Indirect production, high. The alias is still the real Undici package and is the preferred path on Reroute's supported Node versions. Only updating Undici 7 would leave the primary path vulnerable. | Upgrade **only E2B to 2.60.0**, the first examined release declaring `undici ^8.12.2`; lock resolves **8.12.3**, with no `undici8` alias. SDK public API typechecks, mocks and installed import probes must pass. |
| E2B → glob 13.0.6 → minimatch 10.2.6 | Indirect production, moderate: [glob cancellation bypass](https://github.com/advisories/GHSA-3g3m-2fjr-6pw4), [minimatch pattern amplification](https://github.com/advisories/GHSA-g3hv-75wg-m249). glob ≤14.0.1 / minimatch <11.0.1 affected. Arbitrary pattern/tree input and reliance on cancellation, or minimatch optimization ≥2, are relevant conditions. Reroute does not expose E2B's template/glob upload API; its E2B adapter writes individual files. Reachability reduction is **not** a clean audit. | **Unresolved:** even E2B 2.62.0 still declares `glob ^13.0.6`; no glob 13/minimatch 10 backport exists at review time. Fix requires an upstream E2B release adopting glob ≥14.0.2/minimatch ≥11.0.1 or a separately reviewed SDK change. Do not force an incompatible major merely to clear the report. |
| Pi Coding Agent 1.1.0 → minimatch 10.2.6 / Undici 8.10.2; Pi AI → OpenAI → shared Undici | Development/host, moderate + high; **not** installed with the standalone Reroute consumer. Actual Pi host installations remain affected independently of Reroute. | **Unresolved:** even Pi 1.4.0 pins these versions. Keep the tested development SDK, record the full audit failure, and update when upstream accepts fixed versions. Reroute cannot repair its host by bundling a second SDK or publishing root-only overrides. |

Undici fixes start at **7.30.1 / 8.12.2** for the observed branches. Findings include [response decompression crash](https://github.com/advisories/GHSA-xjj6-f33q-929c), [dropped TLS verification options](https://github.com/advisories/GHSA-2m5v-pch6-74hx), [redirect/cache disclosure](https://github.com/advisories/GHSA-4w7w-5xrc-9h26), plus EventSource, SRI, WebSocket buffering, renegotiation and decompression-interceptor issues recorded in the audit JSON. They have different preconditions; not every feature is exercised by Reroute.

**Current repository result:** production **3 moderate, 0 high/critical** (E2B/glob/minimatch); full tree **4 moderate, 1 high** (including the pinned Pi host/development dependencies). This is intentionally **not a zero-vulnerability claim**. `npm run audit:dependencies` runs both audits, preserves both exit codes, and fails on production high/critical or invalid/unavailable audit reports. Full-tree high findings are explicitly printed and retained rather than silently ignored. Normal `npm audit --omit=dev` still exits nonzero for the remaining moderates; CI uses the documented `--audit-level=high` release threshold.

## Installation scripts and least privilege

| Package | Why present | Script / policy |
|---|---|---|
| `@google/genai` | Pi AI development/host tree only | Published `preinstall` prints a no-op. No runtime permission needed; absent from clean Reroute production install. |
| `esbuild` | Pi/Chord development tree only | `postinstall` locates/verifies a platform binary and may download a fallback. Reroute is built with TypeScript and does not need this script. Absent from production. Do not omit platform optional packages and assume esbuild still works. |
| `cbor-extract` | Modal → cbor-x optional native acceleration | Install probes/builds a native helper. Not needed for correct CBOR operation; cbor-x has a JS fallback. Clean installation tests explicitly exercise that fallback with `CBOR_NATIVE_ACCELERATION_DISABLED=true`. Skipping acceleration may affect performance, not protocol correctness. |
| `protobufjs` | Modal and gRPC runtime | Published postinstall emits dependency-version advice; no compilation/download is required. Installed protobuf encode/decode is tested with all install scripts disabled. |

No consumer script approval is required for Reroute's tested CLI/extension path. The pack check tests **both** unmodified npm default policy and `--ignore-scripts`, without `allowScripts`, global approvals, or `dangerously-allow-all-scripts`. npm 11 behavior depends on its minor version/configuration; npm 12 blocks unapproved dependency scripts by default. Pending-script notices are not automatically approved. Reroute has no install lifecycle hooks. If a user independently needs esbuild or native acceleration for another package, review that package/version and use the package manager's **per-package/project** approval, not a global allow-all. This PR requires no such exception.

## Other projects' npm locks

Reroute's own lock migration is separate from remote project support. `src/prepare.ts` continues recognizing shrinkwrap; `src/snapshot.ts` continues hashing **both** npm locks and runtime declarations. npm ≤11 gives shrinkwrap precedence. npm ≥12 ignores it and can silently use a different package-lock when both exist.

The supervised remote preparation now queries the **actual remote** `npm --version` when shrinkwrap exists, refuses npm ≥12 or an unknown/failed version, and does not run either `npm ci` or the main task after refusal. The guard is inside existing timeout/cancellation/process supervision. It never changes project locks, installs another npm, falls back to mutable `npm install`, or retries locally. Use a reviewed npm 11 image or let the project maintainer migrate the lock deliberately. Static environment hints describe this version distinction without claiming `packageManager` selects the actual remote runtime.

References: [npm 12 release](https://github.com/npm/cli/releases/tag/v12.0.0), [npm 11 shrinkwrap rules](https://docs.npmjs.com/cli/v11/configuring-npm/npm-shrinkwrap-json), [npm 12 ci](https://docs.npmjs.com/cli/v12/commands/npm-ci), [npm script policy](https://docs.npmjs.com/cli/v12/using-npm/config/), [Pi package/host dependency contract](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md).

## Automatic updates and release gate

`.github/dependabot.yml` checks npm and Actions weekly. Only ordinary patch/minor utility/development updates are grouped; Daytona, E2B, Modal and Pi SDKs stay separate, as do majors and security fixes. No major is ignored. Security PRs are GitHub-alert-driven rather than waiting for the version-update schedule. Repository Dependabot alerts and automated security fixes were enabled and confirmed through the GitHub API; auto-merge remains disabled. The new weekly configuration becomes active once this PR is merged into the default branch.

Every PR must pass review and CI. No workflow merges PRs, creates Releases or publishes npm. CI covers Node 22/24 × npm 11.21.1/12.2.1 on Linux (npm 12 itself requires Node ≥22.22.2 or ≥24.15.0 in these lines), preserving SSH, Pi, panel/PTYS, AFT coexistence, safety/budget tests and benchmarking. A separate Windows job builds and checks lock discovery, routing and the CLI entry; it is **not** a claim that Linux execution targets or native Unix commands work on Windows.

`check:package` packs the actual commit, checks the allowlisted files, installs in fresh consumer roots (no starting lock), checks production high/critical audit independently, then runs CLI setup/doctor/explain/local and real Pi registration in a separately installed host. It verifies there is no Pi SDK/GenAI/esbuild/TypeScript in the consumer tree, exercises production SDK imports and CBOR/protobuf, and records actual installed package counts, disk use, runtimes, tarball integrity, source commit and dirty-state in `.artifacts/package-verification.json`. The install/probes use temporary HOME/XDG/Pi directories and empty npm configs, never personal keys or global installations.

### Verification results

Final verification is also attached to the PR CI artifacts. Local runs use WSL2 Linux (`6.18.54.1-microsoft-standard-WSL2`), not native Windows or a cloud sandbox.

- Node **24.22.1 / npm 11.19.1**: locked `npm ci --ignore-scripts`, typecheck/build, full offline suite **291 passed / 2 skipped / 0 failed**. The skips were the optional real-AFT cases; installing AFT 0.62.0 separately and rerunning coexistence produced **4/4 passing**, both extension orders, no model/cloud calls.
- Real consumer tarball, Node 24/npm 11, both default-policy and no-scripts: CLI smoke and independent Pi **1.4.0** registration passed. **222 installed packages** including Reroute; **123,468 KiB** allocated `node_modules` per install on this filesystem. Consumer audit: **4 moderate / 0 high**; the extra moderate versus repository production audit is the installed `@onedotmint/reroute` metavulnerability through E2B, not an extra underlying advisory. Platform optional packages and registry changes can change counts; these are measurements, not size budgets.
- The initial attempt to independently install old Pi **1.1.0** failed **before extension loading**: its `^1.1.0` internal ranges now resolve newer Pi modules, whose codemode declarations no longer export `MCP_TYPESCRIPT_PREAMBLE`. We did not hide this with consumer overrides. Existing locked Pi 1.1 tests pass; independent current-host testing uses Pi 1.4.0, whose internal dependencies are pinned. This upstream old-host fresh-install problem is not evidence of a Reroute registration failure.
- Node **24.22.1 / npm 12.2.1**: plain `npm ci` under npm's default script policy, typecheck/build, all **291 passed / 2 optional real-AFT skips**, and CLI smoke passed. npm explicitly reported all four unapproved scripts blocked; none was approved or needed. The separate actual-AFT run above covers the skipped cases.
- Node **22.23.4 / npm 11.21.1** and **22.23.4 / npm 12.2.1**: locked install, typecheck/build, **293/293 passing with zero skips** in each run, including actual AFT, real npm lock fixtures, panel PTYs and CLI smoke.
- Node **22.23.4 / npm 12.2.1** clean tarball: both script policies passed, including the assertion that CBOR native acceleration is actually disabled before SDK initialization. **222 packages / 123,472 KiB**, consumer audit **4 moderate / 0 high**. This run also exposed npm 12 changing `npm pack --json` from an array to a package-name-keyed object; the checker now supports both verified formats.
- Additional exact Node/npm matrix results are recorded in the PR and CI artifacts. Windows acceptance and real localhost SSH run in CI; until those jobs complete they are **pending**, not locally passed. The local machine has no sshd, and the existing server fixture deliberately only runs on disposable GitHub Linux runners.
- Live Daytona/Modal/E2B, real billing, model tool-selection, and real remote cloud image compatibility: **not run**. No account/resource inspection, provisioning, deployment, publication or merge was performed.
