Mastra's npm packages were backdoored in 88 minutes. The speed is the lesson.
One hijacked npm account backdoored 140+ Mastra packages in ~88 minutes - faster than a takedown reaches you. The fix is on your side of the install.
On June 17, one hijacked npm maintainer account turned Mastra — a widely used open-source TypeScript framework for building AI agents — into a malware delivery system, and it moved faster than human review or a registry takedown is built to move. An attacker who had taken over one maintainer account with publish rights across the entire @mastra scope republished more than 140 packages, each quietly carrying a new dependency, in roughly 88 minutes. By the time anyone could plausibly have read an alert and reacted, the backdoored versions were already live and installable. So the decision rule this incident hands developers is uncomfortable but plain: stop assuming that anything upstream — the registry, the maintainer, your feed of advisories — will catch a poisoned package before it reaches you, and move your defenses to the one moment you actually control, which is the install itself.
That is a sharper conclusion than the usual "audit your dependencies," and the clock is the reason. This was not a slow burn that careful watching would have caught. It was a burst.
What actually happened
The mechanics, documented in close agreement by StepSecurity, Socket, and Microsoft Threat Intelligence, are almost boringly efficient. The dependency the attacker slipped into each manifest was easy-day-js, a typosquat of dayjs, the date library that pulls tens of millions of downloads a week and whose name a tired developer would never look at twice. A clean, harmless version of easy-day-js was published on June 16 to sit on the registry and look established. The weaponized version went up at 01:01 UTC on June 17. About eleven minutes later the first package was republished with it attached, and over the next hour and a half — roughly 88 minutes from that first poisoned package to the last — more than 140 packages across the mastra and @mastra names were reissued the same way. @mastra/core alone draws more than 918,000 downloads a week; the affected packages together exceed 1.1 million. That is real exposure, even though no count of actually compromised machines has been established — these are simply not obscure packages.
None of this required a clever zero-day. It required one account that could publish to a whole namespace, and a script to do the publishing.
Why the install step is the whole game
The detail that matters most is where the malicious code ran. easy-day-js carried a postinstall hook, so its payload executed during npm install, before a single line of your code imported anything. You did not have to use Mastra, call an agent, or ship to production. Pulling the dependency tree was enough.
What ran was an obfuscated dropper that first disabled TLS certificate validation, then pulled a second-stage implant from an attacker-controlled server and launched it as a detached background process before deleting itself. The implant set up persistence three different ways depending on the host — a Windows registry run key, a macOS launch agent, a Linux systemd user unit — then went hunting: it carried a hardcoded list of 166 crypto wallet browser extension IDs to look for, MetaMask and Phantom among them, and it collected browser history from Chrome, Edge, and Brave. Picture a small team that runs npm install against the latest @mastra packages in continuous integration on every green commit. There is no user to click a warning in that flow and no review gate after resolution; the dependency resolves, the hook fires on a build agent that probably holds cloud and registry credentials, and — assuming the dropper reaches its server and runs — the compromise is finished before a human sees the pipeline turn red.
The speed is the part that should change behavior
Eighty-eight minutes is the number to sit with. Most of the defensive advice that follows an incident like this assumes a human is in the loop with time to act: watch your advisory feed, review new versions, wait for the registry to pull the bad releases. At this tempo most of that posture is decorative. Automated scanners did flag this campaign quickly — that is how the timeline above is even known — but a flag is not a removal: a registry-wide takedown, when it comes, still arrives after a window in which any build that ran could have pulled the bad versions into its lockfile, and the advisory you read at 9 a.m. describes something that finished overnight. There is one exception, and it is the important one — automated blocking that runs on your own machine the moment a dependency resolves, fast precisely because it does not wait for anyone upstream. That is the lever, and it is where this is heading.
The real risk, then, is not how this one account fell — the sources confirm a takeover but not the method. Maintainer accounts will keep getting compromised one way or another, and namespace-wide publish rights will keep existing. The risk is structural: npm is built so that the distance between "a version was published" and "its code is running on your laptop" can be a few minutes and one automated trigger. That is the same install-time-trust weakness behind the self-spreading Miasma worm that hit Red Hat's npm packages weeks earlier — a different entry point, the identical shape: a poisoned package that does its damage the instant it is fetched. AI tooling makes the target richer, because the reflex around it is to install fast and often, but the design flaw predates every agent framework on the registry.
What you actually control
If upstream can't be trusted to be fast enough, the leverage is entirely on your side of the install, and in practice it is unglamorous. The single highest-value setting is telling your package manager not to run install scripts by default, so a postinstall hook can't fire the moment a dependency resolves. Pinned exact versions and a committed lockfile do the next bit of work — install with npm ci, or your package manager's frozen-lockfile mode, so a build uses exactly the locked tree and changes only when you change it, not when a maintainer ships at 1 a.m. A deliberate cooldown between a release appearing and your project adopting it helps too, and @latest has no business in CI. None of that saves you, though, if a lockfile diff that quietly adds an unfamiliar transitive dependency — an easy-day-js you never asked for — gets waved through as noise instead of read as the security event it is. And if you suspect a machine already pulled one of these versions, don't reason about it abstractly: verify what's on the box. A read-only exposure scan is the right first move, which is exactly what a tool like Bumblebee is for; if it turns up a confirmed match, the job shifts from checking to containment — isolate the machine and rotate any credential it could reach.
None of that stops the next maintainer account from being stolen. It is not supposed to. The point is to break the automatic path between someone else's mistake and your credentials, because that path is the only part of this you can actually govern. Better detection on the registry side helps, but a registry-wide takedown is by definition a reaction, and a reaction that still has to reach every machine loses to a script that finishes in under two hours. Build as if the warning will always arrive late, because at the speed these campaigns now move, it usually will.
Source links
- StepSecurity — Mastra npm Packages Compromised Using easy-day-js
- Socket — 140+ Mastra npm Packages Compromised in Coordinated Supply Chain Attack
- Microsoft Security Blog — From package to postinstall payload: Inside the Mastra npm supply chain compromise
- npm — dayjs package page (weekly download volume the typosquat traded on)