Chapter 07Lesson 04150–200 min

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.

401 / 403Stale metadataRegistry mismatchLifecycle scriptsDiagnostics

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

Evidence-first npm/Nexus diagnosis
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?

A 401 appears after moving to an isolated npmrc. What should you inspect before roles?

A fresh client sees the same stale upstream metadata as every other client. Which cache becomes the stronger suspect?

Why is @candidate insufficient forensic evidence?

Why should a suspicious package be tested with --ignore-scripts during repository diagnosis?

Next lesson

Integrated npm checkpoint

Build a fresh topology, publish two versions, move a channel tag, prove clean-client resolution and tarball identity, and document why the internal scope cannot accidentally publish to the public registry.

Official references and version notes

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.