Between late 2024 and early 2025, Snyk identified eight malicious npm packages that exploited pre- and post-install scripts to execute arbitrary commands on developer machines. Although these packages were removed from the npm registry, the incident highlights a significant vulnerability in how teams manage open-source dependencies.
What Happened
Eight npm packages contained malicious code activated during installation. These packages used npm's lifecycle hooks—specifically preinstall and postinstall scripts—to execute commands without user consent. When a developer ran npm install on a project with one of these packages, the malicious code executed with the user's permissions.
The packages were crafted to appear legitimate, making them difficult to distinguish from the millions of other packages in the npm ecosystem without automated analysis.
Timeline
- Late 2024 to Early 2025: Malicious packages published to npm registry
- Discovery: Snyk's automated monitoring flagged suspicious behavior in install scripts
- Response: Snyk reported findings; npm removed all eight packages from the registry
- Current status: Packages are no longer available, but systems that installed them remain compromised until remediated
The time between publication and discovery is unknown, leaving uncertainty about how many environments executed this code.
Which Controls Failed or Were Missing
No Review of Transitive Dependencies
Most teams review direct dependencies but often overlook the dependencies of those dependencies. With projects pulling in hundreds of packages, manual review is impractical. These malicious packages could have entered your codebase as a fourth- or fifth-level dependency without appearing in any pull request.
Install Scripts Enabled by Default
npm automatically executes preinstall, install, and postinstall scripts without prompts or warnings. This default behavior allows any package to run shell commands during npm install.
No Automated Scanning in the Install Pipeline
If you're not scanning packages before they reach your node_modules directory, you're trusting every maintainer in your dependency tree. This trust extends to:
- The original author
- Anyone with commit access to the repository
- Anyone who compromised a maintainer's npm credentials
- Anyone who infiltrated the project through social engineering
Missing Dependency Pinning and Lock File Verification
Without lock file verification in CI/CD, a compromised package can infiltrate your build even if it wasn't in your original dependency tree. An attacker can exploit automatic version resolution to introduce malicious code.
What the Standards Require
OWASP ASVS v4.0.3 — Requirement 14.2.1
"Verify that all components are up to date, preferably using a dependency checker during build or compile time."
This requirement assumes visibility into your components. The npm incident shows that visibility alone isn't enough; you need to analyze component behavior, especially during installation.
NIST 800-53 Rev 5 — Control SA-10: Developer Security Testing and Evaluation
"The organization requires the developer of the information system, system component, or information system service to perform security testing/evaluation at development, implementation, and integration stages."
Install scripts represent an integration stage. If your CI/CD pipeline runs npm install without scanning for malicious behavior, you're not meeting this control's intent.
PCI DSS v4.0.1 — Requirement 6.3.2
"An inventory of bespoke and custom software, and third-party software components incorporated into bespoke and custom software is maintained to facilitate vulnerability and patch management."
The inventory requirement extends to transitive dependencies. If you can't enumerate what's in your node_modules directory, you can't maintain the required inventory.
SOC 2 Type II — CC7.2 (Change Management)
"The entity authorizes, tests, and approves system changes prior to deployment."
An install script that executes during npm install is a system change. If your process doesn't inspect and approve these scripts before they run, you're not meeting the control objective.
Lessons and Action Items for Your Team
1. Disable Install Scripts by Default
Add --ignore-scripts to every npm install command in your CI/CD pipeline and local development documentation. Evaluate packages that need install scripts explicitly and add them to an allowlist.
In .npmrc:
ignore-scripts=true
This may break some packages, forcing you to decide which packages get execution privileges.
2. Implement Automated Package Scanning
Add a scanning tool to your CI/CD pipeline that runs before npm install. The tool should flag:
- Packages with install scripts
- Recently published packages (less than 72 hours old)
- Packages from new or unverified maintainers
- Packages with suspicious code patterns
Tools like Snyk Advisor and Socket.dev provide this functionality. Make it a required check in your pipeline.
3. Lock Your Dependencies Completely
Use npm ci instead of npm install in CI/CD. Verify your package-lock.json in code review. If the lock file changes, require an explanation of what changed and why.
Add a CI check that fails if package-lock.json is out of sync with package.json.
4. Audit Your Current Dependencies
Run npm ls to see your full dependency tree. For each package with an install script:
- Review the script code in
node_modules/[package]/package.json - Search for known issues with that package
- Decide whether you trust it or need to find an alternative
Make this a recurring quarterly review.
5. Monitor Your Registry for Anomalies
Set up alerts for:
- New packages added to your dependency tree
- Version updates to existing dependencies
- Changes to install scripts in packages you use
Tools like GitHub Dependabot can send notifications when your package-lock.json changes. Configure them to require approval before merging.
6. Separate Development and Production Credentials
If malicious code runs during npm install on a developer laptop, limit the blast radius. Don't store production credentials in development environments. Use temporary, scoped credentials that expire after each session.
The eight packages Snyk found are gone, but the attack pattern remains viable. Your controls need to assume that malicious packages will appear in the registry again—because they will.



