Skip to content

burn down the cli-test-child-env baseline: 18 spawned children in 17 packages/cli/test files still built from the whole of process.env #11596

Description

@os-zhuang

#11341 landed check:cli-test-child-env, which refuses a bulk process.env copy in a spawner file under packages/cli/test/**. The leaks that predate the gate are recorded in scripts/cli-test-child-env.baseline.json as a shrink-only ratchet. This card is the burn-down.

The gate deliberately did not repair them: sweeping without the mechanism leaves the next file free to reintroduce the shape (#11341's own argument), and two of the files below were held by #11071 in a parallel seat the round the gate landed.

The ledger, as it stands

18 bulk references across 17 files:

filecount
cloud-login-json-ndjson.e2e.test.ts1
generate-agent-retired.e2e.test.ts1
generate-skill.e2e.test.ts1
hook-body-build-reach.e2e.test.ts1
init-created-files-summary.e2e.test.ts1
invocation-loudness.e2e.test.ts1
json-stdout-purity.e2e.test.ts1
login-json-ndjson.e2e.test.ts1
login-json-noninteractive.e2e.test.ts1
migrate-exit-code.e2e.test.ts1
migrate-meta.e2e.test.ts2
qa-empty-glob-exit-code.e2e.test.ts1
serve-app-anchored-optional-import.e2e.test.ts1
serve-app-runtime-hooks.e2e.test.ts1
serve-host-fallback-base.e2e.test.ts1
validate-json-warning-parity.e2e.test.ts1
validate-top-level-strict.e2e.test.ts1

node scripts/check-cli-test-child-env.mjs --list prints this live, with line numbers and the enclosing function, so no worklist has to be maintained by hand — the enumeration in this body is a snapshot and the command is the source of truth.

One card per neighbourhood, not one sweep

The repair is mechanical (env: childEnv({ ... }) in place of the spread) but it is not uniformly risk-free, which is why it is split rather than batched:

  • serve-app-anchored-optional-import.e2e.test.ts is the one that matters most. Its hand-rolled runServeFrom() boots a real os serve with the full stack — better-auth included — so it is the single file in the directory that has the leak and mounts something that reads TEST. It asserts optional-service resolution today, so nothing it checks can currently be softened into a pass, but its assertions are one edit away from being security-relevant. finding: #11341's bare-...process.env worklist is two files short, and one of them spawns a real os serve #11441 is the card that measured it.
  • The serve-* files each need the child re-checked after the strip: the crypto provider persists a minted key to $HOME/.objectstack/dev-crypto-key in development mode, which is why runServe() passes an explicit OS_SECRET_KEY. A file that stops claiming to be a vitest worker may need the same. helpers/serve-process.ts's header carries the measurement, and serve-node-env-production-default going red the moment the leak stopped is the recorded precedent for a pin whose "production posture" was genuine for auth and fake for crypto.
  • generate-skill.e2e.test.ts and generate-agent-retired.e2e.test.ts sit in os g writes NAME.ts for six generators while the type registry declares NAME.TYPE.ts — #11025 closed it for skill alone #11071's neighbourhood — check it has landed before touching them.
  • The remaining login / migrate / validate / init / qa / hook / json-stdout files spawn commands that mount nothing reading the runner family. Those are the cheap batch.

Ratchet mechanics for whoever takes a slice

Lower the count in scripts/cli-test-child-env.baseline.json in the same PR, and delete the key when it reaches 0 — the gate fails on a stale ceiling, in that direction too, deliberately: a ceiling left above reality silently licenses that many new leaks.

Related

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions