Secure Package Management
Hardening NPM and Python dependencies against supply chain attacks. Protect your builds from malicious packages and compromised maintainers.
Most of this is free and straightforward: lockfiles, package-manager policy, and stricter install settings can materially reduce risk without buying an expensive vendor platform.
Recent Notice: Axios npm compromise
Axios maintainers confirmed that [email protected] and [email protected] were malicious npm releases published through a compromised maintainer account on March 31, 2026. Those versions pulled in plain-crypto-js and were live for roughly three hours before removal.
If your lockfile or install logs show either affected Axios version or plain-crypto-js, treat that workstation or CI runner as potentially compromised, not merely out of date.
Quick Check
grep -E "axios@(1\\.14\\.1|0\\.30\\.4)|plain-crypto-js" package-lock.json yarn.lock 2>/dev/null
- Downgrade or repin to
[email protected]or[email protected]. - Remove
node_modules/plain-crypto-js/and reinstall from a known-good lockfile. - Rotate credentials, tokens, and secrets exposed to that machine or CI job.
- Review network and EDR telemetry for connections to
sfrclak[.]comor142.11.206.73:8000. - Prefer delaying newly published packages in CI and on developer workstations so short-lived malicious releases are less likely to land.
This repository's current package-lock.json has an empty packages object and does not show an Axios install, but the broader guidance still matters for any Node environment you operate elsewhere.
Notable Supply Chain Attacks
Real-world incidents that demonstrate why supply chain security is critical:
[email protected] and [email protected], which pulled in plain-crypto-js and dropped a cross-platform trojan. Post-mortemHardening NPM and Python Dependencies: Mitigating Supply Chain Attacks
Open-source supply chain attacks are a significant and evolving threat to modern software development. Attackers frequently target build tools and local developer environments rather than waiting to exploit application code in production. By compromising maintainer accounts, using typosquatting, or exploiting dependency confusion, malicious code is injected directly into public registries.
In both NPM and PyPI, the risky path is any install flow that executes dependency-controlled code: Node lifecycle scripts such as preinstall and postinstall, and Python source builds that invoke PEP 517 backends or legacy setup.py. That code often runs with access to developer secrets, CI tokens, and internal package infrastructure.
Effective hardening combines strict lockfiles, delayed adoption of newly published releases, explicit approval of dependency build steps, and extra isolation for higher-risk installs.
1. JavaScript/TypeScript: Using pnpm
pnpm v10 is a strong default for Node projects because dependency lifecycle scripts are blocked unless you explicitly approve them. It also includes built-in controls for release-age delays, trusted build approvals, and blocking exotic transitive dependency sources. See the pnpm supply chain security guide and the settings reference.
Action: Commit a pnpm Policy to the Repository
Keep the security policy in pnpm-workspace.yaml so developers and CI runners enforce the same rules.
Enable pnpm and Migrate Project
# Enable pnpm via Corepack (Node.js 16.17+)
corepack enable pnpm
# Migrate an existing project
rm -rf node_modules package-lock.json
pnpm import # Generates pnpm-lock.yaml
Project Security Policy (pnpm-workspace.yaml)
# Delay adoption of brand-new releases
minimumReleaseAge: 1440
# Block git/tarball transitive dependencies
blockExoticSubdeps: true
# Fail installs when unreviewed dependency build scripts appear
strictDepBuilds: true
# Approve only the dependency builds you trust
allowBuilds:
esbuild: true
# Optional when your workflow supports pnpm trust signals
# trustPolicy: no-downgrade
pnpm approve-builds to review pending scripts, then commit the resulting allowBuilds policy. Avoid relying on global ignore-scripts=true as the main pnpm workflow in v10; it is broader than necessary and also disables your project's own scripts.
2. Python: Using uv
Standard pip workflows can execute arbitrary code when an installation falls back to a source distribution. That execution path now commonly runs through PEP 517 build backends, not just legacy setup.py files.
uv is a fast Python package installer and resolver that acts as a drop-in replacement for pip. It simplifies dependency locking and strongly prefers wheels, but a high-trust workflow should still refuse source builds unless you explicitly allow them. See the uv compile guide and the uv CLI reference.
Action: Enforce Hashed, Wheel-Only Installs
For the strictest workflow, combine hash enforcement with --no-build so the install fails rather than building from an sdist.
Install uv
# Install uv (Mac/Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh
Workflow for Secure Python Management
Virtual Environment and Lockfile
# Create an isolated virtual environment
uv venv
source .venv/bin/activate
# Generate a strictly hashed lockfile
# Input: requirements.in (e.g., requests==2.31.0)
uv pip compile requirements.in --generate-hashes -o requirements.txt
# Install exactly what is locked
# --require-hashes rejects unhashed entries
# --no-build fails if a wheel is unavailable and a source build would be required
uv pip sync requirements.txt --require-hashes --no-build
Instead of relying on a loose requirements.txt, use uv pip compile to lock exact versions and their cryptographic hashes. If a package is altered in PyPI without a version bump, the hash check will fail and the installation will be blocked. If a dependency is only available as an sdist, treat it as an exception and isolate that install path deliberately.
3. Dedicated Linux Isolation with bubblewrap
For Linux users, bubblewrap (bwrap) adds an extra boundary between install-time code and your workstation. The goal is to expose only the project directory and the cache or store paths you need, while hiding the rest of your real home directory from package scripts.
This is an advanced defense, not a transparent replacement for every install command. It is most useful for higher-risk dependency reviews, or for strict installs that already run from a warm cache or controlled internal registry.
Example Wrapper (~/bin/safe-install)
#!/bin/bash
set -eu
bwrap \
--ro-bind /usr /usr \
--ro-bind /bin /bin \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /etc /etc \
--proc /proc \
--dev /dev \
--tmpfs /tmp \
--dir /work \
--bind "$PWD" /work \
--chdir /work \
--dir /sandbox-home \
--dir /sandbox-home/.cache \
--dir /sandbox-home/.local \
--dir /sandbox-home/.local/share \
--dir /sandbox-home/.local/share/pnpm \
--setenv HOME /sandbox-home \
--setenv XDG_CACHE_HOME /sandbox-home/.cache \
--ro-bind-try "$HOME/.cache/uv" /sandbox-home/.cache/uv \
--ro-bind-try "$HOME/.cache/pnpm" /sandbox-home/.cache/pnpm \
--ro-bind-try "$HOME/.local/share/pnpm/store" /sandbox-home/.local/share/pnpm/store \
--unshare-ipc \
--unshare-pid \
--new-session \
"$@"
Usage
safe-install uv pip sync requirements.txt --require-hashes --no-build
# or
safe-install pnpm install --frozen-lockfile
--unshare-net only when you already have a warm cache or an internal mirror reachable inside the sandbox. Otherwise dependency resolution and downloads will fail. For normal online installs, bubblewrap reduces host exposure but does not replace your package manager policy.
4. Scaling to Enterprise Environments (CI/CD)
Securing local developer machines is only part of the process; build pipelines require the same level of strictness.
Proxy Registries
Avoid pulling directly from public registries like npmjs.org or pypi.org in CI pipelines.
- Route traffic through a private artifact proxy (e.g., JFrog Artifactory, Sonatype Nexus).
- Enable quarantine policies on the proxy to automatically block packages with low reputation scores or newly published versions, effectively enforcing a network-wide release delay.
Enforce Configurations in CI Pipelines
GitHub Action: Enforcing uv Hashes and Wheel-Only Installs
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v2
- name: Install dependencies strictly
run: uv pip sync requirements.txt --require-hashes --no-build
GitHub Action: Enforcing pnpm Policy
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 10
- name: Install with committed policy
run: pnpm install --frozen-lockfile
Summary
Supply chain attacks exploit default configurations and implicit trust in package registries. Moving to pnpm with a committed build policy, using uv with hashed wheel-only installs, delaying newly published package versions, and adding optional Linux isolation with bwrap will significantly reduce exposure while preserving practical developer workflows.