Repository navigation
v0.5.2
Upgrade if you use pkg@latest. On 0.5.1 that silently skipped two security checks.
Two checks were doing nothing
nvx resolved a version only when you gave it none. Name a dist-tag — npm install npm@latest, pkg@next, pkg@beta — and the literal string was carried forward as if it were a version number. Every lookup keyed on it missed:
- the install-script prompt never appeared, so a package that runs code at install time was installed without asking;
- the release-age check never fired, so the supply-chain cooling-off window was not applied;
- the vulnerability scan was run against a version that does not exist, which is why it reported advisories with no descriptions.
The noisy output was the symptom people noticed. The missing prompts were the actual problem. Every dist-tag now resolves, and an exact version wins over a same-named tag.
A consequence worth knowing: a semver range (lodash@^4.17.0) now asks "could not verify registry metadata — proceed?" rather than passing silently. nvx cannot check a version it cannot name, and quietly running no checks is what this replaces. Resolving ranges properly would remove the prompt and is not done yet.
One sandbox could borrow another's egress allowlist
Every nvx sandbox shares one Windows package identity, and Windows scopes its loopback restriction to that identity — so two projects running at once sat in the same namespace. A contained process could scan loopback, find another session's proxy, and reach a host only that project's policy allowed. Each session now mints its own credential, over HTTP and SOCKS both, checked before the allowlist so the accept/reject difference cannot be used to probe what another session may reach.
A platform with no sandbox ran your command anyway
On any Unix that is not Linux or macOS, nvx set a process group, logged "using environment isolation only", and ran the command — while printing "Running in native sandbox". It now refuses, and names --no-sandbox as the deliberate opt-out. No release binaries are built for those platforms, so this is reachable only from source — which is exactly the person who would trust the word "sandbox" without checking.
Other fixes
- A hung contained process blocked every later contained launch. The supervisor was staged under one fixed name, and Windows will not replace a running executable — so a supervisor left alive held that file and every subsequent launch failed with a bare "Access is denied". Staging is now per-build.
- A corrupted supervisor bricked launches permanently, because the reuse check compared only file size. An image error now discards the copy and retries once.
- macOS granted all of loopback in every mode, so
network.mode: offlinewas not offline and contained code could reach any local service. Loopback is now scoped per mode. Unverified at runtime — see below. - A typo in
isolation.network.modesilently gave you more network than you asked for."offlin"fell through to proxy with no warning. Unrecognised modes now warn and normalise. - A failed install pointed at a debug log that had already been deleted. Logs are now copied to
~/.nvx/logs/<session>before the sandbox home is removed, and only on failure. - Shims pointed at whichever binary generated them, so generating them from a source build left every shim depending on a file that could be rebuilt or deleted. They now name the installed nvx.
yarn global addwas not recognised as a global install and failed inside the sandbox with a permission error instead of a clear refusal.- A refused global install still ran a vulnerability scan and asked you to approve it first, for a command that could never have run.
nvx doctornow reports pre-0.5.0 permissions that leave a project writable by any sandbox, and removes them with--fix.
Honesty notes
macOS is still unverified at runtime. The Seatbelt profile's text is asserted by tests, so nvx generates the policy it intends to; no macOS hardware has been observed enforcing it. The loopback fix above is a fix to the generated profile. docs/enforcement-matrix.md marks every macOS row "profile only".
Windows: a contained server is unreachable from the host. nvx npx vite, npx serve and anything else serving a port will bind, report themselves listening, and serve nobody. Windows refuses connections into an AppContainer.
Asynchronous piped output still hangs. Synchronous capture works (that is what made npm install esbuild work); a tool that streams a child's output as it is produced does not. Run it with --no-sandbox.
Upgrading from 0.5.1 is a binary replacement; no state migration.