You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Give harnesses a supported, discoverable way to authenticate against a private package registry from inside the sandbox, and detect when a private-registry auth token is present but unreachable because the file that holds it is protected.
Today ~/.npmrc is a protected path (internal/sandboxprofile/baseline.go:51, protectedCommon()), classified as an "npm token" secret (internal/profileaudit/check.go:141). It is denied by default even under a broader home-dir grant. It is not hard-blocked — it is overridable via filesystem.override_deny (EffectiveProtectedPaths, baseline.go:188) — but nothing surfaces that escape hatch, and the failure it produces is silent.
Why
This is the filesystem/credential counterpart to #148 (which explains blocked registry fetches on the network axis). The two failures stack for the private-registry case:
This issue covers auth: even when the host is reachable and allowed, npm/pip/cargo must read the auth token from ~/.npmrc (or ~/.pypirc, cargo credentials, …), and that read is denied by the protected-path policy. The install fails 401/403at the registry, not at the proxy.
Related to #143 (harness fetching a config-declared plugin into a cold cache): a private-registry plugin can clear the network gate and still fail auth, with no legible cause.
The protection itself is correct — the token is a secret and must not leak to arbitrary sandboxed code. The gap is that there is (a) no documented, scoped way to opt a real private-registry workflow in, and (b) no signal that this is what went wrong.
Detection — can omac detect it?
Not at runtime via the proxy. For HTTPS the proxy splices a raw TCP tunnel and TLS bytes pass through unread (internal/netproxy/server.go:266, handleConnect). It is not a MITM, so it cannot observe the registry's 401/403 auth response or body. The #148 hints fire on the proxy's own deny (403 denyBody) or dial failure (502 upstreamErrorBody) — a registry-issued auth failure is invisible to it. So we cannot bolt private-registry-auth detection onto the #148 network path.
Yes, proactively at launch — this is the tractable path. Mirror #148's isPackageRegistry(host) host detector on the config file instead of the wire:
At sandbox setup, if ~/.npmrc exists, is not listed in filesystem.override_deny, and contains a registry-scoped auth entry (_authToken, //<host>/:_authToken, _auth, _password) pointing at a non-public registry (reuse the private-registry heuristic from feat(netproxy): explain blocked package-registry fetches #148's isPackageRegistry), emit an actionable note:
~/.npmrc holds a private-registry token but is protected by the sandbox; private-package installs will fail auth. To allow it, add ~/.npmrc to filesystem.override_deny, or supply the token via NPM_TOKEN/NODE_AUTH_TOKEN through the env allowlist.
Same shape generalizes to ~/.pypirc (check.go:147) and cargo credentials.
This is a static, read-side heuristic used only to enrich a likely-failing setup, so a false positive is harmless — same design principle #148 applied to its host matcher.
Proposed remedies to document/support
Escape hatch (exists, undocumented):filesystem.override_deny: ["~/.npmrc"] punches the specific hole while keeping every other credential protected.
Env-based token (preferred, no file grant): forward NPM_TOKEN/NODE_AUTH_TOKEN (and pip/cargo equivalents) via the env allowlist and let the harness synthesize a minimal .npmrc in the workdir — no home-dir secret exposure. Aligns with the env-forwarding pattern already used for provider creds.
Document both in the sandbox profile reference alongside the protected-path list.
Scope
Launch-time detector + hint for a protected-but-populated ~/.npmrc (npm first; pypirc/cargo as follow-ups).
What
Give harnesses a supported, discoverable way to authenticate against a private package registry from inside the sandbox, and detect when a private-registry auth token is present but unreachable because the file that holds it is protected.
Today
~/.npmrcis a protected path (internal/sandboxprofile/baseline.go:51,protectedCommon()), classified as an "npm token" secret (internal/profileaudit/check.go:141). It is denied by default even under a broader home-dir grant. It is not hard-blocked — it is overridable viafilesystem.override_deny(EffectiveProtectedPaths,baseline.go:188) — but nothing surfaces that escape hatch, and the failure it produces is silent.Why
This is the filesystem/credential counterpart to #148 (which explains blocked registry fetches on the network axis). The two failures stack for the private-registry case:
npm/pip/cargomust read the auth token from~/.npmrc(or~/.pypirc, cargo credentials, …), and that read is denied by the protected-path policy. The install fails401/403at the registry, not at the proxy.Related to #143 (harness fetching a config-declared plugin into a cold cache): a private-registry plugin can clear the network gate and still fail auth, with no legible cause.
The protection itself is correct — the token is a secret and must not leak to arbitrary sandboxed code. The gap is that there is (a) no documented, scoped way to opt a real private-registry workflow in, and (b) no signal that this is what went wrong.
Detection — can omac detect it?
Not at runtime via the proxy. For HTTPS the proxy splices a raw TCP tunnel and TLS bytes pass through unread (
internal/netproxy/server.go:266,handleConnect). It is not a MITM, so it cannot observe the registry's401/403auth response or body. The#148hints fire on the proxy's own deny (403 denyBody) or dial failure (502 upstreamErrorBody) — a registry-issued auth failure is invisible to it. So we cannot bolt private-registry-auth detection onto the #148 network path.Yes, proactively at launch — this is the tractable path. Mirror #148's
isPackageRegistry(host)host detector on the config file instead of the wire:~/.npmrcexists, is not listed infilesystem.override_deny, and contains a registry-scoped auth entry (_authToken,//<host>/:_authToken,_auth,_password) pointing at a non-public registry (reuse the private-registry heuristic from feat(netproxy): explain blocked package-registry fetches #148'sisPackageRegistry), emit an actionable note:~/.pypirc(check.go:147) and cargo credentials.This is a static, read-side heuristic used only to enrich a likely-failing setup, so a false positive is harmless — same design principle #148 applied to its host matcher.
Proposed remedies to document/support
filesystem.override_deny: ["~/.npmrc"]punches the specific hole while keeping every other credential protected.NPM_TOKEN/NODE_AUTH_TOKEN(and pip/cargo equivalents) via the env allowlist and let the harness synthesize a minimal.npmrcin the workdir — no home-dir secret exposure. Aligns with the env-forwarding pattern already used for provider creds.Scope
~/.npmrc(npm first; pypirc/cargo as follow-ups).override_denyvs env-token, with the security tradeoff spelled out.Non-goals
~/.npmrc(would defeat the secret protection).Related: #148 (network-axis registry hints), #143 (cold-cache plugin fetch).