npm Repositories, Scoped Packages, Metadata, Tokens, Proxying, and JavaScript Supply Chains: Diagnostics, Failure Modes, Security, and Performance
Diagnose npm/Nexus failures from evidence: credential leakage, wrong publish registry, 401/403, public namespace shadowing, stale metadata, mutable dist-tags, lifecycle-script risk, and client/proxy cache confusion.
Learning objectives
- Apply an evidence-first diagnostic sequence to npm/Nexus incidents.
- Distinguish wrong registry selection from authorization failure, package absence, proxy cache state, and client-cache state.
- Recognize token leakage and repair credential state without copying secrets into logs.
- Diagnose stale metadata and public shadowing without globally disabling security controls.
- Separate performance symptoms caused by npm cache, Nexus proxy cache, upstream latency, database/blob IO, and lifecycle scripts.
Incident rule. Preserve the original npm/HTTP error and redact secrets before sharing it. Do not delete Nexus database rows/blob files, globally disable TLS, broaden roles to administrator, or clear every cache as a first response.
1. The diagnostic sequence
flowchart TD E[Preserve npm/HTTP evidence] --> V[Confirm Nexus/npm versions] V --> C[Inspect registry, scope mapping, npmrc, cache] C --> T[Inspect hosted/proxy/group type + order] T --> A[Inspect authorization/realm] A --> M[Inspect package metadata + component/assets] M --> P[Inspect proxy/negative cache + upstream] P --> S[Inspect DB/blob/disk/logs/tasks] S --> X[Apply least-destructive correction] X --> R[Verify with fresh client config/cache]
Keep the sequence because identical-looking npm errors can arise from different layers. A 404 can mean wrong registry, negative cache, absent version, package hidden by authorization, or an upstream miss. A 401 can mean missing registry-scoped auth, wrong realm/login behavior, or invalid credentials. Diagnose the layer before changing policy.
2. Failure mode: token leaked in .npmrc or logs
An auth line committed to Git or printed in CI logs must be treated as compromised, even if the repository is currently private. The first action is not “delete the log and continue”; revoke/rotate the credential at the source, remove it from active client state, and then sanitize retained evidence.
set -eu
# Scan disposable files for auth-key names without printing matching values.
python - <<'PY2'
from pathlib import Path
import re, os
root=Path(os.environ.get('LAB','/tmp'))
pat=re.compile(r'(_authToken|_auth|password|token)', re.I)
for p in root.rglob('*'):
if p.is_file() and p.stat().st_size < 2_000_000:
try: txt=p.read_text(errors='ignore')
except Exception: continue
if pat.search(txt): print('sensitive-key pattern:', p)
PY2
For this lab, destroy the isolated npmrc and re-run
interactive login if another publication is needed. In production,
rotate the service credential and remove it from source/history/log
retention according to incident policy.
3. Intentionally broken example: publish to the group in Community Edition
Take the synthetic package and deliberately point
npm publish at academy-ch07-group. In
Community Edition, the group has no Pro writable member feature.
Preserve the HTTP/client error.
set +e
cd "$LAB/pkg"
npm publish --ignore-scripts --registry="$NX_URL/repository/academy-ch07-group/" 2>&1 | tee "$LAB/evidence/intentional-group-publish-failure.txt"
RC=${PIPESTATUS[0]}
set -e
printf 'Expected non-zero exit: %s
' "$RC"
Repair: publish to
academy-ch07-hosted. Do not upgrade permissions or
alter the group merely to hide the endpoint mistake. If a Pro
organization intentionally uses writable npm groups, verify the
group's designated writable hosted member and treat the group URL as
a routed write facade—not independent storage.
4. 401/403: separate authentication from authorization
401 usually means the request did not present
acceptable authentication. Check the exact registry URL, path-scoped
auth line, active npm Bearer Token Realm, and whether the isolated
npmrc is actually the one npm loaded.
403 means an authenticated identity may lack the
required repository privilege or a policy denied the operation. Do
not respond to either status by granting administrator.
printf 'userconfig=%s
' "${NPM_CONFIG_USERCONFIG:-unset}"
printf 'cache=%s
' "${NPM_CONFIG_CACHE:-unset}"
npm config get registry
npm config get @learner-example:registry || true
# Redacted view of the isolated config.
sed -E '/(_auth|_authToken|password|token)/Id' "$LAB/npmrc" | tee "$LAB/evidence/npmrc-routing-redacted.txt"
5. Stale metadata: client cache or Nexus proxy cache?
If a new public version or changed public dist-tag is not visible, start with a fresh npm cache. If the fresh client still sees the same old metadata, inspect Nexus proxy metadata age, negative cache, remote health, and upstream response. On a disposable proxy, a targeted Invalidate Cache action can expire metadata/negative-cache state so the next request rechecks upstream. It does not require deleting cached blob files.
| Observation | Likely layer | Next check |
|---|---|---|
| Only one workstation sees old metadata | npm local cache/config | Fresh NPM_CONFIG_CACHE and userconfig. |
| All fresh clients see old public metadata through Nexus | Nexus proxy metadata cache | Proxy cache age, invalidate-cache on disposable repo, upstream response. |
| Package was previously 404 and remains missing shortly after upstream publication | Negative cache | Original miss timestamp and negative-cache TTL/invalidation. |
| Internal hosted package tag differs from expectation | Hosted metadata/dist-tag state | Read package metadata from hosted/group; inspect publisher history. |
6. Public package shadows internal intent
Symptom: @learner-example/internal-tool or an unscoped
internal name resolves to bytes from the public proxy. Preserve
npm view ... dist.tarball, group member order, scope
registry mapping, and lockfile URLs. Then determine whether the
client bypassed Nexus, the group searched public before internal, or
internal content was absent/unauthorized.
Repair with the smallest correct control: fix scope mapping, reorder members, add/adjust a routing rule where appropriate, or close direct public egress. Do not “repair” by manually uploading the public tarball into the internal repository under the same name.
7. Mutable dist-tag mistaken for artifact identity
A pipeline records
@learner-example/ch07-demo@candidate. Later
candidate moves from 1.0.0 to 1.1.0, and an audit can
no longer infer which bytes ran. The repository is functioning
correctly; the evidence model is wrong. Record exact version plus
integrity/digest and treat the tag as contextual channel metadata.
8. Lifecycle scripts: repository success can still execute untrusted code
A proxied package can contain install lifecycle scripts. If the
investigation concerns repository routing rather than package
execution, reproduce with npm view,
npm pack, or
npm install --ignore-scripts in a disposable directory.
Do not execute a suspicious package's lifecycle scripts merely to
prove Nexus can download it.
Supply-chain boundary: checksum/integrity validation, repository authorization, provenance, vulnerability intelligence, and script-execution policy answer different questions. Never disable one control because another passed.
9. Performance: identify the causal layer
| Slow path | Evidence | Potential cause |
|---|---|---|
| First public metadata/tarball request | Nexus request/log timing + upstream timing | Upstream/network latency, proxy miss, TLS/DNS. |
| Repeated public request still slow | Proxy/cache evidence | Metadata freshness rules, cache misses, blob IO, database latency. |
| Only one client slow | npm timing/cache logs | Client cache, filesystem, DNS, local antivirus, lifecycle scripts. |
| Internal hosted tarball slow for everyone | Nexus metrics + blob/database/disk | Blob IO, disk pressure, JVM/DB contention, network. |
| Install CPU spike after download | npm lifecycle output | Package scripts/build steps rather than repository transport. |
Knowledge check
A publish to academy-ch07-group fails in Community Edition. What should you fix first?
The publish endpoint: use the hosted repository. Writable npm group publication is a Pro feature and should not be emulated by widening permissions.
A 401 appears after moving to an isolated npmrc. What should you inspect before roles?
The exact userconfig file, registry URL/path, registry-scoped auth entry, active npm realm, and whether login succeeded for that endpoint.
A fresh client sees the same stale upstream metadata as every other client. Which cache becomes the stronger suspect?
The Nexus proxy metadata/negative cache or upstream itself, because the client cache was isolated.
Why is @candidate insufficient forensic evidence?
It is a mutable dist-tag; later movement changes what it resolves to. Record exact version and integrity/digest.
Why should a suspicious package be tested with --ignore-scripts during repository diagnosis?
To separate repository transport/routing behavior from executable lifecycle-script behavior and reduce risk while investigating.
Official references and version notes
- Nexus Repository Download and current 2026 release notes — re-check the current 3.95.x self-hosted baseline before executing the lab.
- Sonatype: npm Registry — hosted, proxy, and group behavior.
- Sonatype: Configuring npm — registry configuration through Nexus.
- Sonatype: Publishing npm Packages — hosted publication and the Pro-only writable-group option.
- Sonatype: npm Security — npm Bearer Token Realm/login and basic-auth alternatives.
- Configurable Repository Fields — npm writable-group, proxy, cache, and repository options.
- npm Registry documentation and .npmrc — scope routing and registry-scoped authentication.
- npm publish and npm dist-tag — version immutability, integrity, and mutable channel labels.
Version-sensitive statements were rechecked against Sonatype and npm
primary documentation on 2026-08-26. The mandatory lab assumes Nexus
Repository Community Edition 3.95.0 and a current npm 12 client;
record npm --version and
node --version locally because npm/Node compatibility
evolves independently of Nexus. Nexus 3.87+ requires Java 21 for
supported self-hosted deployments. Re-check current release/support
pages before executing the lab.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.