What Happened
Between October and December 2024, attackers used stolen npm credentials to publish malicious versions of over 400 packages. These releases contained a variant of the Mini Shai-Hulud credential-stealing worm, which executed automatically through preinstall hooks. Microsoft Threat Intelligence identified the attack after observing packages with valid provenance attestations that were still malicious.
The attackers didn't exploit a vulnerability in npm's infrastructure. They compromised legitimate maintainer credentials and used those to publish packages through the normal workflow. Because the packages were published using GitHub Actions from authentic repositories, they generated valid provenance attestations, making the malware appear trustworthy.
Timeline
The attack unfolded over two months:
Initial Compromise: Attackers obtained valid npm publishing credentials for package maintainers.
Publication Phase: Using the stolen credentials, attackers published malicious versions of packages. Each malicious release included preinstall hooks that executed the Mini Shai-Hulud worm.
Propagation: The worm executed during npm install, attempting to steal additional credentials from developer workstations and CI/CD environments. These stolen credentials enabled the attackers to compromise more packages.
Detection: Microsoft Threat Intelligence flagged the attack after noticing the pattern of malicious packages with valid provenance attestations.
Mitigation: npm worked with maintainers to remove malicious versions and reset compromised credentials.
Which Controls Failed or Were Missing
Credential Rotation and Monitoring: The initial credential theft suggests inadequate credential lifecycle management. Once stolen, the credentials remained valid long enough to compromise hundreds of packages.
Preinstall Hook Restrictions: npm allows packages to execute arbitrary code through preinstall hooks during installation. Your CI/CD pipeline runs these hooks automatically, giving malware immediate execution.
Dependency Installation in Publishing Workflows: Many teams run npm install in the same GitHub Actions workflow that publishes packages. This creates a circular trust problem: you're installing potentially malicious dependencies while holding publishing credentials.
Provenance as Security Theater: Teams treated provenance attestations as a security control rather than a transparency feature. The attestations proved the packages came from the expected GitHub repository, but they couldn't detect that the repository's credentials were compromised.
Cached Dependencies in CI: GitHub Actions and similar CI platforms cache dependencies to speed up builds. If you install a malicious package once, it persists in your cache and executes in subsequent runs, even after the package version is removed from npm.
What the Relevant Standard Requires
NIST 800-53 Rev 5 IA-5(1) requires organizations to enforce minimum password complexity, change passwords at defined intervals, and protect passwords from unauthorized disclosure. The credential theft that enabled this attack violates these baseline controls.
NIST Cybersecurity Framework v2.0 PR.AC-1 calls for identities and credentials to be issued, managed, verified, revoked, and audited. Your npm tokens need the same lifecycle management as any other privileged credential: rotation schedules, scope limitations, and revocation procedures.
ISO/IEC 27001:2022 Annex A.8.3 addresses media handling and requires secure disposal of information. Publishing tokens stored in GitHub Actions secrets or local configuration files need secure deletion when no longer required.
PCI DSS v4.0.1 Requirement 6.4.3 mandates that scripts running on payment pages come from trusted sources and haven't been modified. While this requirement targets payment card environments, the principle applies to your build pipeline: you can't execute untrusted code in a context that holds publishing credentials.
Lessons and Action Items for Your Team
Separate Installation from Publishing: Run npm install in a workflow that has no access to publishing tokens. Build your package, run tests, and generate artifacts in this isolated environment. Only after verification should you move the built artifact to a separate workflow that publishes it. This workflow should never install dependencies.
Upgrade to npm CLI 12: Microsoft's recommendation isn't optional. npm CLI 12 includes security improvements that limit the damage from compromised packages. If you're running older versions in CI, you're accepting known risks.
Implement Min-Release-Age: Configure your package manager to reject packages published within the last few hours. This gives the community time to identify and report malicious releases before they reach your builds. Add this to your .npmrc:
min-release-age=3h
Treat Preinstall Hooks as Malicious by Default: Configure your CI environment to run npm install --ignore-scripts during builds. If you need specific lifecycle scripts, whitelist them explicitly and document why they're required.
Rotate Publishing Tokens Monthly: Set a calendar reminder. Generate new tokens, update your CI secrets, verify the new tokens work, then revoke the old ones. If a token is compromised, you've limited the exposure window.
Scope Tokens to Specific Packages: Don't use a single token with publish access to all your packages. If you maintain multiple packages, create separate tokens for each one. When a token is compromised, you've contained the blast radius.
Monitor for Unexpected Package Versions: Set up alerts when new versions of your dependencies are published. Services like Snyk, Socket, or GitHub Dependabot can notify you within minutes of a new release. A package that hasn't updated in months suddenly publishing a new version deserves investigation.
Audit Your GitHub Actions Cache: Run actions/cache@v3 with a unique cache key that includes your dependency lock file hash. This forces cache invalidation when dependencies change. Don't let a malicious package persist in your cache after you've removed it from your dependency tree.
Stop Trusting Provenance Attestations as Security: Provenance tells you where a package came from. It doesn't tell you whether that source was compromised. Treat provenance as an audit trail, not a security boundary.
The attackers in this incident didn't need sophisticated exploits. They stole credentials and used normal publishing workflows to distribute malware. Your defense isn't exotic tooling; it's basic credential hygiene, process isolation, and skepticism about automated trust.



