A former contributor’s forgotten npm account just trojanized roughly 142 packages with about 5.5 million combined monthly downloads — and the attacker never touched a single line of the actual Mastra source code. On June 17, 2026, someone walked into the @mastra npm scope through a credential that should have been revoked years ago, republished nearly every package in the namespace with a malicious dependency, and turned npm install into a cryptocurrency wallet stealer and a remote access trojan for anyone unlucky enough to resolve a fresh tree that day. If you maintain a JavaScript project that pulls anything from @mastra/*, stop reading and check your lockfile. Then come back, because the mechanics here matter more than the immediate IOCs.
How a Dormant npm Account Became a Mass Compromise
The entry point was not a zero-day, not a phishing kit, not a clever dependency confusion trick. According to the Snyk advisory tracking this as SNYK-JS-EASYDAYJS-17353313, the attacker took over ehindero, a real former Mastra contributor whose account had published legitimate alpha releases of @mastra/core in late 2024 and early 2025 and then gone dormant. Scope publish access was never revoked, and an email change to ehindero2016@tutamail[.]com suggests account takeover rather than insider action.
This matters because npm does not expire scope publish permissions based on inactivity. One stale credential equals full write access across an entire namespace, forever, until somebody actively prunes it. For teams shipping any package scope with more than one maintainer, that is a standing invitation. The blast radius is whoever you ever trusted, plus whoever later compromises them.
Imagine you’re a startup that brought on three contractors in 2024 to ship an SDK, then quietly moved on. Those npm accounts still have publish rights on your scope today. If any one of them reused a password, recycled a recovery email, or simply abandoned the account to a recycled handle, an attacker can republish your entire library tomorrow morning and tag it latest. Your CI will obediently pull the armed version on the next build.
Our take: the next twelve months of npm supply chain attacks will look more like this and less like typosquats. The dormant-maintainer attack surface is enormous, fully indexed by the registry, and almost no project audits it.
Why the easy-day-js Dropper Pattern Is So Hard to Spot
The attacker did not modify the Mastra source tree. Mastra’s own remediation PR (#18056) verified the repository was clean. Instead, every malicious tarball added exactly one line to the published package.JSON: a dependency on easy-day-js, a dayjs impersonator that ships a bundled dayjs.min.js and a near-identical package description.
The sequencing is the elegant part. On June 16, 2026, a clean easy-day-js@1.11.21 was published as cover. The next day, easy-day-js@1.11.22 shipped with the malware and was tagged latest. Because the injected dependency used a caret range, any normal npm install resolved straight to the armed version. The setup.cjs postinstall hook then disabled TLS verification with NODE_TLS_REJECT_UNAUTHORIZED='0', fetched a second-stage payload over HTTPS from a raw IP (23.254.164[.]92:8000) using a self-signed certificate, spawned it detached, and deleted itself.
This defeats two defenses most teams quietly rely on. Code review never sees the malicious dependency because it never enters the Git history — it is injected at publish time. And HTTPS-based egress monitoring won’t flag the fetch because TLS verification has been silently disabled in the install process.
If you’re a team using ephemeral CI runners to build and deploy, every runner that touched a @mastra package on or after June 17 had its environment scraped: cloud keys, npm tokens, LLM API keys, SSH keys, browser wallet extensions for MetaMask, Phantom, Solflare, Coinbase Wallet, OKX, and Keplr — all base64-encoded into a JSON beacon and exfiltrated. Persistence was established as a macOS LaunchAgent, a Linux systemd user service, or a Windows PowerShell stage under C:\ProgramData\NodePackages, beaconing every ~10 minutes to a separate RAT C2.
Our take: postinstall scripts are now a liability that outweighs their convenience for most consumer projects. Expect npm config set ignore-scripts true to move from “paranoid hardening tip” to default guidance within a year.
What Lockfile Hygiene Actually Bought You This Time
The headline number — @mastra/core pulls roughly 4 million downloads per month and mastra adds about 1.5 million more, per the npm registry — overstates the real exposure. The practical question is whether your build resolved one of the malicious versions during the exposure window, and that comes down to lockfile discipline.
If a committed lockfile pinned you to a pre-incident version and your CI ran npm ci, the caret range was never re-resolved and you’re almost certainly fine. If you had no lockfile, a loose range, or a fresh install or lockfile regeneration on or after June 17, the caret range pulled easy-day-js@1.11.22 and the postinstall ran. That distinction is the entire game.
If you’re an agency running monorepos for multiple clients, the cost-benefit shifts immediately: enforce npm ci, ban lockfile-free installs in CI, and add a pre-commit hook that fails the build if easy-day-js ever appears in a resolved tree. That discipline isolated some teams entirely. It’s the same principle behind a secure, auditable supply chain — provenance and reproducibility, not vibes.
Our take: the projects that survived this with zero exposure all had boring lockfile policies. The ones that got burned were the ones using npm install in CI “to stay current.”
The Pattern Is Now a Playbook — and It Will Escalate
This isn’t a one-off. The technique — clean-then-armed dependency, postinstall dropper, TLS-bypass-to-raw-IP fetch, cross-platform wallet stealer with self-deleting installer — closely mirrors the Axios npm compromise Snyk analyzed earlier in 2026, which Microsoft Threat Intelligence attributed to Sapphire Sleet (BlueNoroff). Attribution for the @mastra incident is not confirmed, but defenders should treat the pattern as repeatable infrastructure, not a one-off.
The attacker also published versions above the legitimate latest for several packages (for example, @mastra/schema-compat@1.2.12, @mastra/react@1.0.1, @mastra/voice-playai@0.12.2), forcing Mastra to pin explicit higher versions in PR #18060 rather than rely on ordinary patch bumps. Unpublishing is no longer a safe remediation; you have to forward-roll past the attacker’s version numbers.
Ecosystem-level controls need to fill that gap. Mastra generated SLSA provenance on CI publishes, but it was not required, so a plain token could still publish without attestations. Until consumers enforce provenance at install time, an attacker with a stale credential can ship unattested packages that look indistinguishable from real releases. The question mirrors the call between blockchain and a traditional database for an audit log: provenance is only useful if something rejects payloads that lack it.
Our prediction: by the end of 2026, at least one major npm-adjacent tool (likely Snyk, GitHub, or pnpm) will ship an opt-in mode that refuses to install packages without SLSA provenance from a known publisher identity. Within 18 months, that mode will be the default in serious CI templates.
FAQ
Q: Am I affected if I never directly installed @mastra/core?
A: Possibly. The attack hit roughly 142 publishable packages across the @mastra scope plus mastra, create-mastra, and mastracode. If any of your transitive dependencies pulled one of them on or after June 17, 2026, the easy-day-js postinstall hook could have run on your build host. Grep your lockfile for easy-day-js — it should never legitimately appear.
Q: What should I do if I find evidence of compromise on a developer laptop or CI runner?
A: Treat the host as fully compromised. Rotate every credential the host could reach — cloud keys, CI secrets, LLM API keys, npm tokens, SSH keys. Move any cryptocurrency wallets from browser extensions (MetaMask, Phantom, Solflare, Coinbase Wallet, OKX, Keplr) to a fresh wallet on a clean device. Remove persistence artifacts (macOS LaunchAgent at ~/Library/LaunchAgents/com.nvm.protocal.plist, Linux systemd unit at ~/.config/systemd/user/nvmconf.service, or Windows staging under C:\ProgramData\NodePackages) and reimage where practical.
Q: Why didn’t unpublishing the bad versions fix this?
A: Because for several packages, the attacker published versions above the legitimate latest. Mastra had to forward-roll clean releases past the attacker’s version numbers in PR #18056 and explicitly pin higher versions in PR #18060. If you’re remediating, upgrade to a known-good Mastra release rather than pinning downward — and verify it isn’t a stranded attacker version.
Key Takeaways
- Audit publish permissions across every npm scope you own this quarter and revoke any account that has not published in the last 12 months — dormant maintainer accounts are now an actively exploited attack surface.
- Disable npm install scripts by default with
npm config set ignore-scripts trueand explicitly allow them only for builds that genuinely need them; this neutralizes the entire class of postinstall droppers. - Make
npm ciagainst a committed lockfile non-negotiable in CI, and add a build-failing check foreasy-day-jsand known-bad IOCs in resolved dependency trees. - Start requiring SLSA provenance on the packages you consume in critical pipelines; tooling support is uneven today but consumer-side enforcement is the only durable defense against publish-time tarball injection.
- Treat any incident where a postinstall ran with a hostile payload as a credential and wallet exposure event, not just a dependency issue — rotate everything the host could reach and reimage when feasible.