Operational notes Observatory

ChainDrop: the npm worm that survives removal by rewriting VS Code and Claude Code configuration

7 min read

Black-and-white photograph of rows of metal chain links, each one hooked into the next
Every compromised package hooks into another: it is the same logic by which ChainDrop propagates.

On 5 August 2026 Italy’s national CSIRT (ACN) published bulletin BL01/260805/CSIRT-ITA on “ChainDrop: a self-propagating worm campaign in the npm ecosystem”, rated criticality “Medium (64.97)”. The summary: over 400 compromised npm packages, from different maintainers and organisations. Among the names involved: keyv 6.0.0, flat-cache 6.1.24, file-entry-cache 11.1.6, cache-manager 7.2.10, cacheable 2.5.1, cacheable-request 13.0.20, @cacheable/memory 2.2.1, @cacheable/node-cache 3.1.2, @cacheable/utils 2.5.1, @cacheable/net 2.1.1, ecto 5.0.1. The bulletin is under Sources, below — we do not translate it wholesale here, we read it for whoever has to decide what to do next. It exists only in Italian; every quotation from it below is our own translation of the original text.

The trust boundary isn’t the model: it’s the package

Anyone worrying about which artificial intelligence to adopt, and not about what an npm install actually runs, is watching the wrong door. The compromised releases introduce a preinstall script that launches setup.mjs, which loads the Math_Symbol.js payload (or equivalent variants). The bulletin is unambiguous about when this happens: “the execution of the malicious content takes place during the package installation phase, before the npm procedure completes” (our translation). There is no moment at which anyone decides whether to trust it: execution precedes the decision.

The payload, moreover, does not behave the same way everywhere: it distinguishes a development workstation from a CI/CD runner, and on the latter it “can remain attached to the build process to access the secrets, temporary tokens and credentials available within the pipeline’s own context”. The attack does not target the single laptop: it targets the point in the infrastructure where credentials are most concentrated and least watched — the pipeline that runs while no one is looking. We had already documented a cycle that precedes human oversight, there with an offensive agent acting without an operator step by step; here the operator does not even get the chance to intervene, because the package has not yet finished installing.

Persistence reaches the agent’s configuration too

The most useful point in the bulletin, for anyone running AI-assisted development tools, sits in the section on persistence and evasion: the malware modifies “configuration files associated with development environments and GitHub repositories, including components linked to Visual Studio Code and Claude Code, in order to maintain persistence on systems and enable further executions even after the original dependency has been removed”. On top of that come advanced obfuscation and encrypted exfiltration of the information gathered.

This should be read with technical coolness, without alarmism and without laying blame on Microsoft or Anthropic: the flaw is not in their products. It is that an untrusted process — the preinstall script of a compromised package — was able to write the configuration of a trusted tool. An editor’s or an agent’s configuration is a file like any other: whoever controls it decides what the tool runs on the next restart, even after the offending dependency has been removed from the project — uninstalling the package does not touch what that package has already written elsewhere on disk. This is exactly the extra surface for anyone running development agents on their own systems: not because the agent is vulnerable in itself, but because its configuration is reachable by any process with sufficient permissions on the same machine.

Stolen credentials publish more packages

What the malware collects is not generic: “environment variables, npm tokens, GitHub tokens, SSH keys, cloud credentials and secrets used in automation pipelines”. And it is what is done with them that makes ChainDrop a worm, not a simple theft: the credentials obtained are used “to access repositories, cloud services and npm accounts, enabling the automated publication of further compromised packages”. It is a supply chain that feeds on itself — every compromised maintainer becomes the vector to the next one.

That is why the list of eleven packages above is a snapshot, not a perimeter, and ACN itself warns of it: “owing to the self-propagating mechanism, the perimeter of affected versions is extremely dynamic. It is recommended not to limit verification to the version numbers indicated above alone, but to extend the check to any release published within the campaign’s time window”. A check run today against eleven precise names is reassuring, and nothing more: the question that still holds, three weeks from now, is a different one.

The right question isn’t “which packages” — it’s “which token”

Asking “are we exposed to these eleven packages?” is the wrong question, because the answer expires the moment you get it. The one that holds is: which token, present on which machine, could publish in our organisation’s name? And if we revoked it today, could we say with certainty what stops working? If the second answer doesn’t exist — if no one in the company knows which builds, which deployments, which automations depend on that token — the problem isn’t ChainDrop: it’s that the credential inventory doesn’t exist, and ChainDrop has only made that visible.

The bulletin’s mitigations should be read with that in mind, not as a box to tick once. The first step is blocking automatic execution: --ignore-scripts during analysis or remediation stops preinstall and postinstall hooks from firing on their own; updating npm — and evaluating min-release-age — cuts the risk of automatically pulling a release that has only just been published, still within the window before anyone has flagged a compromised package. The second is searching: inside package-lock.json, yarn.lock and pnpm-lock.yaml, to check whether compromised names and versions are already in the codebase, and on the filesystem, for files such as setup.mjs, Math_Symbol.js or math_init.js. The third is pinning known-good versions with overrides or resolutions, so an automatic update doesn’t reintroduce what has just been excluded.

Then comes the part that costs more, and is therefore put off more often: revoking and regenerating npm tokens, GitHub credentials, cloud keys and CI/CD secrets accessible from the affected host; clearing the caches; verifying the artefacts produced during the exposure window; rebuilding images and CI/CD runners from trusted sources, not from a backup that might already contain the payload. And finally checking, not assuming, that changes to repositories, automated workflows and development-tool configuration files — “e.g. VS Code and Claude Code extensions and settings” — are the ones you authorised.

How we solve this

An up-to-date dependency inventory, with lockfile hashes, is exactly the kind of control we wrote about regarding the Cyber Resilience Act and the SBOM obligation: applied to ChainDrop, it becomes the way to answer “did this version touch us?” in hours, not weeks. Compliance takes a precise shape: a register of which credential has access to what and lives on which machine, documented proof — with date and outcome — of every token rotation, and detection of unauthorised changes to development-tool configuration files. Not an audit done once and filed away: the record to show an inspector, kept current because the control actually runs.

The same set-up that keeps that register is the one that, one level up, brings the organisation’s scattered data — repositories, pipelines, inventory, logs, documents, business systems — into a single operating model, on which AI agents execute decisions with a human operator in command: for large enterprises, defence, government and healthcare, where a documented assessment of ICT suppliers is not a bureaucratic exercise but the basis on which a purchase gets authorised.

This needs saying in full, and it is a point that strengthens our credibility rather than weakening it: having the model inside your own perimeter, with your own weights, does not protect you from this. Owning the model does not mean owning the packages that run it: the software supply chain — npm, PyPI, every language’s registry — still has to be governed, regardless of where the model runs. We do it in both modes: on-premise, on self-contained machines that don’t require deep integration into the client’s network, or on a dedicated cloud with a data centre in Italy — always with governance run together with you.

Do you know which token, on which machine, could publish in your organisation’s name — and what stops working if you revoke it? Let’s talk in a session at no cost: together we’ll go through the credential and dependency inventory you have in production today.

Sources