Secure Package Management

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 '"version": "(1\.14\.1|0\.30\.4)"|plain-crypto-js' package-lock.json 2>/dev/null
grep -E 'axios@.*(1\.14\.1|0\.30\.4)|plain-crypto-js' yarn.lock pnpm-lock.yaml 2>/dev/null

The Axios post-mortem recommends a simpler grep axios@... pattern, which works well for yarn.lock and for catching plain-crypto-js broadly. This page uses lockfile-type-specific patterns because npm's package-lock.json stores resolved versions in a packages object's version field, not in [email protected] strings, so the generic pattern can miss a compromised Axios entry in npm lockfiles.

  • 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[.]com or 142.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:

March 31, 2026
Axios npm compromise
Compromised maintainer account led to malicious publication of [email protected] and [email protected], which pulled in plain-crypto-js and dropped a cross-platform trojan. Post-mortem
Late February to March 2026
TeamPCP Supply Chain
Multi-stage campaign targeting Trivy, KICS, LiteLLM, and Telnyx via compromised maintainers.
August to October 2025
PhantomRaven npm
126 typosquatted packages, 86,000+ downloads targeting developer credentials.
Late November 2025
Shai-Hulud 2.0
796 packages compromised, 25,000+ repositories affected.
September 2025
Shai-Hulud npm Worm
Self-replicating worm compromised hundreds of npm packages starting around September 14.
September 8, 2025
Chalk/Debug
18 high-download packages compromised, live for approximately two hours.
August 26 to 27, 2025
Nx Attack
Malicious versions published, secrets exfiltrated rapidly within 24 hours.
March 29, 2024 (Discovered)
xz Utils Backdoor
Multi-year maintainer infiltration, backdoor inserted in releases 5.6.0 and 5.6.1 (February to March 2024). Disclosure
January 31 to April 1, 2021
Codecov
Bash uploader backdoored, undetected for just over two months.
2018
EventStream
Malicious dependency chain led to cryptocurrency theft attempts via compromised maintainer.

Hardening 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. This includes npm lifecycle hooks such as preinstall, install, and postinstall. Dependency install and build scripts execute arbitrary code with developer or CI privileges. These scripts typically have 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 install and build scripts, and extra isolation for higher-risk installs. Controls must be enforced in CI and shared config, not only local developer environments. These attacks typically result in credential theft, CI compromise, or unauthorized package publishing.

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 install and 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
Approving dependency install and build scripts: Use 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 manager and resolver. Its native workflow uses pyproject.toml to declare dependencies, uv.lock to lock them, and uv sync to reproduce that environment. The pip-compatible subcommands are still useful for existing requirements.txt-based repositories or for export paths, but they are not the primary uv model. A high-trust workflow should still refuse source builds unless you explicitly allow them. See the uv projects guide, the locking and syncing guide, and the uv CLI reference.

Action: Prefer Native Locks, Export When Needed

For the default workflow, commit pyproject.toml and uv.lock, then use uv sync to reproduce that environment. If another tool still requires requirements.txt, export from the lockfile rather than treating hand-maintained requirements files as the source of truth. For stricter installs, combine that with --no-build so the sync 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

Native uv Project Workflow

# Create an isolated virtual environment
uv venv
source .venv/bin/activate

# Add direct dependencies to pyproject.toml
uv add requests==2.31.0

# Resolve and write uv.lock
uv lock

# Reproduce exactly what is locked
# --locked refuses to update the lockfile
# --no-build fails if a wheel is unavailable and a source build would be required
uv sync --locked --no-build

If you need a requirements.txt for a scanner, legacy deploy target, or another downstream tool, export it from the locked state instead of maintaining a separate primary workflow. If a dependency is only available as an sdist, treat it as an exception and isolate that install path deliberately.

3. 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 Lockfile Sync

steps:
  - uses: actions/checkout@v4
  - name: Install uv
    uses: astral-sh/setup-uv@v2
  - name: Install dependencies strictly
    run: uv sync --locked --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 committed native locks and strict sync behavior, and delaying newly published package versions will significantly reduce exposure while preserving practical developer workflows.