On June 16, 2026, at 07:05 UTC, an npm account named sergey2016 published easy-day-js@1.11.21. The package was clean. It was a fully functional copy of the legitimate dayjs date library, mirroring its author name, version numbering, homepage, repository URL, and license word for word. Its only purpose was to sit there long enough to look credible.
Eighteen hours later, at 01:01 UTC on June 17, easy-day-js@1.11.22 appeared. Twenty-six minutes after that, the ehindero npm account began republishing 143 packages across the @mastra namespace with the malicious dependency baked in. The campaign ran for 88 minutes. ehindero was a legitimate former Mastra contributor. Their scope access had simply never been revoked.
How the attack worked
Installation triggered a postinstall hook that executed an obfuscated script called setup.cjs. The first stage disabled TLS certificate verification, wrote local marker files, downloaded a second-stage payload from an attacker-controlled server, launched it as a detached process, then deleted itself. The install appeared to complete normally.
The second stage was a cross-platform Node.js remote access trojan. JFrog Security Research, who discovered and analyzed the campaign, found it collected system reconnaissance on arrival: username, hostname, OS and architecture, running processes, and installed applications. It then harvested browser history hostnames from Chrome, Brave, and Edge, and inventoried which of 166 known cryptocurrency wallet browser extensions were installed. That last step signals the campaign’s likely priority target: developers holding crypto wallet access or high-value AI infrastructure credentials.
Persistence was written before any data left the machine. On macOS, the RAT registered a LaunchAgent at ~/Library/LaunchAgents/com.nvm.protocal.plist. On Linux, it created a systemd user service at ~/.config/systemd/user/nvmconf.service. On Windows, it wrote a Registry Run key named NvmProtocal. All three survive a reboot. C2 traffic was directed to 23.254.164.92 and 23.254.164.123.
Beyond the initial collection, the RAT opened a remote module execution channel. The attacker could push arbitrary Node.js or shell commands to any compromised machine and receive output back. The recon was the first move. The channel was the foothold.
What was affected
Mastra is a JavaScript and TypeScript framework for building AI applications. @mastra/core alone had more than 918,000 weekly downloads at the time of the attack. The broader namespace was pulling an estimated 8 million installs per week across roughly 29 million monthly downloads. Mastra environments routinely hold LLM API keys, cloud provider credentials, CI/CD tokens, and database connection strings.
- easy-day-js@1.11.22: the malicious dependency, published June 17, 2026 at 01:01 UTC. Version
1.11.21was clean. - 143 @mastra packages: republished with the bad dependency starting 01:27 UTC on June 17, including
@mastra/core,@mastra/ai-sdk,@mastra/auth, and the full@mastra/voice-*family. - Safe versions: @mastra packages predating the June 17 publish window do not contain
easy-day-js. npm pulled malicious versions and reverted latest tags after the campaign was identified.
What makes this attack different
Publishing a clean version first is a deliberate technique. A package with a publication timestamp and an existing version history does not trigger newness-based detection. By the time 1.11.22 appeared, easy-day-js already looked like an established minor-version library. Socket flagged it within six minutes anyway. JFrog Curation customers using an immaturity policy were protected within 24 hours. Both catches came from behavioral signals rather than from recognizing the package as new.
The ehindero account is the other thread worth pulling. The attacker did not compromise Mastra’s codebase. They used a real account with real publish rights that had gone stale. Access reviews feel administrative until a former contributor’s credentials become the entry point for an 88-minute mass compromise. Revoking scope access when someone leaves a project is not a hygiene task; it is a blast radius decision made in advance.
What teams should do
Any npm install that resolved easy-day-js@1.11.22 as a transitive dependency should be treated as a full compromise. Start here:
- Check lock files for easy-day-js. Search
package-lock.jsonoryarn.lockfor the stringeasy-day-js. Its presence confirms the malicious dependency was resolved at install time. - Scan for persistence artifacts. On macOS, look for
~/Library/LaunchAgents/com.nvm.protocal.plist. On Linux, check~/.config/systemd/user/nvmconf.service. On Windows, inspect the Registry Run keyNvmProtocal. - Rotate all credentials that were accessible during the session. LLM API keys, cloud tokens, npm publish tokens, CI/CD secrets, and cryptocurrency wallet credentials should all be cycled, in that order.
- Review network logs for C2 contact. Filter outbound traffic for
23.254.164.92and23.254.164.123. Either address appearing in logs confirms the second stage connected. - Reinstall from clean versions. Remove
easy-day-jsfrom dependency manifests and lock files, pin @mastra packages to versions predating June 17, and run a fresh install.
The longer pattern here is not typosquatting. A dependency that published yesterday, from a single account, with a postinstall script, is worth pausing on regardless of how familiar its metadata looks. Teams that flag transitive dependencies by publication age and account history catch this category of attack before the install runs. The postinstall hook is a legitimate feature that gets misused at a steady clip, and npm has signaled plans to restrict it in v12. Until that ships, the hook remains the fastest route from a malicious package to a compromised machine.