# You installed a malicious package. What now?

> A practical incident-response order of operations for developers who have just found a compromised package in their dependency tree - what to rotate, in what order, and what not to bother with.

- Published: 2026-08-03
- Last updated: 2026-08-03

The instinct is to uninstall the package and move on. That is the one response that reliably does not work, because the package already ran. Here is a more useful order of operations.

## First: assume execution already happened

The large majority of malicious packages we track execute at **install time**, not at import time - a `postinstall` hook on npm, a `setup.py` or build hook on PyPI. That matters because it changes what you are dealing with.

You are not asking "is this code reachable from my application". You are asking "what could a process running as me, on this machine, with this environment, have taken". A cancelled install, a failed build, a container that was thrown away thirty seconds later - all of them still ran the payload.

## The order that actually matters

Work outwards from the most valuable and most reusable credentials.

1. **Rotate registry tokens first.** An npm or PyPI publish token is the one credential that turns your compromise into someone else's. This is precisely how single-package incidents become fifty-package worms. Do this before anything else.
2. **Rotate cloud and CI credentials.** AWS keys, GCP service accounts, `GITHUB_TOKEN`, CI secrets, anything that was in the environment. Assume every environment variable readable by that process is now public.
3. **Rotate developer credentials.** SSH keys, GPG keys, and any `.npmrc`, `.pypirc`, `.aws/credentials`, or `.env` file on the machine. Several campaigns specifically hunt these paths.
4. **Then** deal with the package itself - pin to a safe version, reinstall from a clean lockfile.

Do the rotation **from a different machine**. If the payload installed a persistent credential stealer, rotating from the compromised host just hands over the new credentials too.

## Clear the caches - this is the step people skip

Public takedown does not reach your infrastructure. After the registry removes a malicious version, these will happily keep serving it:

- Private mirrors and proxies: Artifactory, Nexus, Verdaccio, devpi, AWS CodeArtifact, Google Artifact Registry
- CI caches keyed on lockfile hash - the compromised tarball is in the cache, and the hash still matches
- Docker layer caches containing the install step
- Local `~/.npm` and `~/.cache/pip`

We see this repeatedly: an organisation cleans up, then reinfects itself a week later from its own cache. Purge the specific package versions from every one of these before you rebuild.

## Work out the blast radius

Once the bleeding has stopped, scope it:

- **Which builds ran?** Check CI history for every job that installed the affected version. Each one is a separate exposure with its own secret set.
- **Which developers pulled it?** Anyone who ran `install` while the version was in the lockfile, not just whoever added it.
- **What egress happened?** If you have DNS or network logs from build agents, look for connections to unfamiliar hosts around the install timestamps. Many incident write-ups list specific indicators of compromise worth grepping for.
- **Did anything publish?** If a registry token leaked, check whether any of your own packages were published during the window.

## What is not worth your time

- **Reading the payload first.** Understanding the specific malware is interesting, but it is slower than rotating and rarely changes the answer. Rotate first, read later.
- **Deciding it was "only a dev dependency".** Dev dependencies install on developer laptops and CI agents, which is where the credentials are. The production/dev distinction does not protect you here.
- **Hoping the version range saved you.** If the compromised version satisfies your range and you installed during the window, you got it. Check the lockfile rather than reasoning about the range.

## Frequently asked questions

### Do I need to wipe the machine?

For most install-time credential stealers, rotating credentials and clearing caches is proportionate. Rebuild if the advisory describes persistence - a background daemon, a scheduled task, a modified shell profile - or if the host handles anything sensitive enough that you would rather not reason about it.

### Nothing looked broken. Was I actually affected?

Almost certainly you would not notice. These payloads are written to be silent - the install succeeds, the build passes, and the exfiltration is a single HTTPS request among many. Absence of symptoms is not evidence.

### How do I find every project that has it?

Scan the lockfiles rather than trusting memory. Transitive dependencies are the common case, so a repo can be affected without the package appearing anywhere in its package.json.

## Next step

Check a lockfile now: https://dependencywatch.io/check

## Related guides

- [Does npm audit detect malicious packages?](https://dependencywatch.io/guides/does-npm-audit-detect-malware) - npm audit finds known vulnerabilities in legitimate packages. It is much weaker at finding deliberately malicious ones, and this explains why - plus what to run instead.
- [How to tell if an npm package is malicious](https://dependencywatch.io/guides/how-to-tell-if-an-npm-package-is-malicious) - Practical signals for judging whether an npm package is safe to install - what actually predicts trouble, what looks alarming but does not, and how to check before you install.

---

Canonical page: https://dependencywatch.io/guides/what-to-do-after-installing-a-malicious-package
Markdown version: https://dependencywatch.io/guides/what-to-do-after-installing-a-malicious-package.md
Site index for agents: https://dependencywatch.io/llms.txt
DependencyWatch.io is operated by Precursor Security (https://precursorsecurity.com).
