Skip to main content

A Forgotten Contributor Account Compromised the Entire Mastra npm Package Scope

Written by
Headshot of Marian Corneci

Marian Corneci

June 16, 2026

0 mins read

An attacker republished the entire @mastra npm scope on June 17, 2026, slipping a single malicious dependency into 143 packages and counting, including @mastra/core, which pulls roughly 4 million downloads a month and has hundreds of dependent projects. The injected dependency, easy-day-js, is a dayjs lookalike whose install hook disables TLS verification, downloads a second-stage payload from a raw IP address, and runs a cross-platform cryptocurrency stealer in the background. This attack on an npm package was made possible due to a former contributor account whose scope access was never revoked.

Action required: if you installed any @mastra package, or anything that depends on one, in a fresh or updated tree on or after June 17, 2026, treat this as a credential and wallet exposure event and read on.

UPDATE: Snyk added a new compromised package @mastra/node-speaker , version 0.1.1.

TL;DR

  • Incident: npm scope takeover of @mastra, mass republish with an injected malicious dependency

  • Malicious dependency: easy-day-js@1.11.22 (a dayjs impersonator acting as a dropper)

  • Snyk advisory: SNYK-JS-EASYDAYJS-17353313, Embedded Malicious Code, affects easy-day-js 1.11.21 and 1.11.22

  • Affected packages: essentially the entire @mastra scope, republished on June 17, 2026; Mastra's own remediation covers 142 publishable packages

  • Notable versions: @mastra/core@1.42.1, mastra@1.13.1, create-mastra@1.13.1

  • CVE / CVSS: none assigned; this is an active supply chain incident, and the Mastra source code itself was not modified

  • Payload: cross-platform cryptocurrency wallet stealer plus a remote access trojan with persistence

  • Status: easy-day-js has been removed from npm, ehindero is no longer an owner of the scope, and Mastra has forward-rolled clean versions (PR #18056). @mastra/core latest is back to a clean 1.42.0. Treat any compromised version you already resolved as a host-exposure event.

How the attack worked

The foothold was a stale maintainer credential. The @mastra scope was published to using the npm account ehindero, a real former Mastra contributor. The npm registry's publish history records legitimate alpha releases of @mastra/core from this account in late 2024 and early 2025, after which it went dormant. Its scope access was never revoked.

The account shows signs of takeover rather than insider action, including an email change away from the contributor's original address to ehindero2016@tutamail[.]com. The mechanics here are worth sitting with: npm does not expire scope publish permissions on inactivity, so one stale maintainer credential was enough to push to every package in the scope. Project hygiene, not a zero-day, is the root cause.

The carrier: one line in every package.json

The attacker did not touch the Mastra source code. The poison was injected into the published tarballs at publish time, and Mastra's own remediation PR confirms the repository was verified clean of easy-day-js across every package.json and lockfile (PR #18056). Each malicious release on npm added exactly one line to the published package.json:

"easy-day-js": "^1.11.21"

The dependency is never imported anywhere in the Mastra source. It exists purely to pull in a trojan at install time. The caret range is the trick. The attacker first published a clean easy-day-js@1.11.21 on June 16, a byte-for-byte dayjs impersonator down to the bundled dayjs.min.js and the package description. The next day, they published easy-day-js@1.11.22 with the malware and tagged it latest. Because 1.11.22 satisfies ^1.11.21, a normal npm install resolves straight to the armed version.

Then, in a burst on June 17, 2026, the attacker republished the remaining scope, each package containing the same injected dependency. Mastra's own remediation enumerates the blast radius precisely: its emergency changeset forward-rolls 142 publishable packages across the @mastra/* scope plus mastra, create-mastra, and mastracode (PR #18056). Public reporting put the count anywhere from 80 to 144, depending on the snapshot, but the vendor's own figure is the one to anchor on, and the takeaway is the same: essentially, the entire scope was hit.

For a handful of packages, the attacker even published versions above the legitimate latest (for example, @mastra/schema-compat@1.2.12, @mastra/react@1.0.1, @mastra/voice-playai@0.12.2), which later forced Mastra to pin explicit higher versions rather than ordinary patch bumps to supersede them (PR #18060).

The malware dropper: TLS off, fetch, detach, delete

easy-day-js@1.11.22 ships a setup.cjs that runs as a postinstall hook. On install it:

  1. Disables TLS certificate validation by setting NODE_TLS_REJECT_UNAUTHORIZED='0', so an HTTPS fetch succeeds against a self-signed certificate on a raw IP.

  2. Writes marker files (~/.pkg_history, ~/.pkg_logs) to track installs.

  3. Downloads a second-stage payload from https://23.254.164[.]92:8000/update/49890878 (Hostwinds-hosted).

  4. Writes it to the temp directory and spawns it as a detached, hidden background process with no console output.

  5. Deletes itself to reduce forensic traces.

The payload: a multi-platform crypto stealer and RAT

The second stage (SHA256 221c45a790dec2a296af57969e1165a16f8f49733aeab64c0bbd768d9943badf) is an obfuscated cryptocurrency wallet stealer, the same payload class Snyk has tracked in earlier npm compromises. It reads Chrome, Brave, and Edge profiles looking for a hardcoded list of cryptocurrency wallet browser extensions, including MetaMask, Phantom, Solflare, Coinbase Wallet, OKX, and Keplr. Alongside wallet data it collects username, hostname, OS and architecture, Node version, installed applications, browser history, and the running process list, then base64-encodes the lot into a JSON beacon.

It also establishes persistence disguised as Node tooling and beacons to a separate RAT C2 (23.254.164[.]123/49890878) roughly every 10 minutes, using a self-signed wolfSSL test certificate (CN=www.wolfssl.com, expired January 2018):

  • macOS: LaunchAgent at ~/Library/LaunchAgents/com.nvm.protocal.plist

  • Linux: systemd user service ~/.config/systemd/user/nvmconf.service

  • Windows: PowerShell execution staged under C:\ProgramData\NodePackages

The clean-then-armed dependency, the setup postinstall dropper, the TLS-bypass-to-raw-IP fetch, and the crypto-stealer payload closely mirror the Axios npm compromise that Snyk analyzed earlier in 2026, down to the install-time dropper that self-deletes. Microsoft Threat Intelligence attributed that earlier campaign to the actor it tracks as Sapphire Sleet (BlueNoroff). Attribution for this incident specifically is not confirmed, and we will not speculate further here; the pattern is worth noting for defenders, not asserting as fact.

Impact analysis

@mastra/core alone pulls about 4 million downloads a month (npm registry) and has hundreds of dependent projects; mastra adds roughly 1.5 million more (npm registry). With 142 packages republished across the scope, the aggregate monthly download volume runs into the tens of millions, so the potential blast radius is large.

The practical exposure is narrower than that headline number, and it is worth being precise about it. You are at risk only if a build or developer machine resolved one of the malicious versions during the exposure window. Lockfiles are the deciding factor and the safe, reproducible pattern for dependencies in npm projects:

  • Pinned via a committed lockfile to a pre-incident version: an npm ci would not have pulled the malicious release. You are likely fine, but verify.

  • No lockfile, a loose range, or a fresh install / lockfile regeneration on or after June 17: the caret range would have resolved to the armed easy-day-js@1.11.22, and the postinstall hook would have run.

Because the payload executes at install time, CI runners, ephemeral build agents, and developer laptops are all in scope, not just production servers. Any of those hosts could have leaked credentials and wallets.

Detection: Are you exposed?

1. Check whether easy-day-js ever entered your dependency tree. It should never legitimately appear:

npm ls easy-day-js

grep -r "easy-day-js" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null

2. Audit the @mastra versions you resolved. Compromised releases were published June 17, 2026 and lack SLSA provenance. Inspect what your lockfile actually pinned:

npm ls @mastra/core mastra create-mastra

3. Scan with Snyk. Snyk flags the malicious package as SNYK-JS-EASYDAYJS-17353313 (Embedded Malicious Code):

snyk test

snyk monitor

4. Hunt for host artifacts on any machine that ran an install during the window:

#Dropper markers

ls -la ~/.pkg_history ~/.pkg_logs 2>/dev/null

#Persistence

ls -la ~/Library/LaunchAgents/com.nvm.protocal.plist 2>/dev/null      # macOS

ls -la ~/.config/systemd/user/nvmconf.service 2>/dev/null             # Linux

dir "C:\ProgramData\NodePackages"                                      # Windows

5. Check egress logs for connections to 23.254.164[.]92:8000 or 23.254.164[.]123, or anything in 23.254.164.0/24.

For a hands-on walkthrough of finding and fixing a scope-wide npm compromise of this shape with Snyk, this remediation demo covers the workflow end to end:

Shai-Hulud NPM Attack: Remediation with Snyk (a hands-on remediation workflow for a mass-republish npm supply chain attack).

Remediation

If easy-day-js is absent and your lockfile pinned pre-incident @mastra versions: confirm with the detection steps above, keep your lockfile pinned, and move on.

If you resolved a malicious version, treat any affected host as compromised.

  1. Upgrade to a clean version; do not just pin downward. Mastra responded by forward-rolling clean releases for all 142 publishable packages and moving the latest dist-tag past the compromised versions (PR #18056, PR #18049), rather than relying on unpublishing. Update to the current clean release (for example, @mastra/core@1.42.0 or later), regenerate a clean lockfile, and remove any trace of easy-day-js. Because the attacker published some versions above the legitimate latest, double-check that the version you land on is a known-good Mastra release and not a stranded attacker version.

  2. Rotate every credential the host could reach: cloud keys, CI secrets and tokens, LLM and other API keys, npm tokens, and SSH keys. Mastra is an agent framework, so LLM provider keys are especially likely to be present.

  3. Move cryptocurrency wallets: if a browser profile on the machine held a targeted wallet extension, assume the seed and keys are exposed and transfer funds to a fresh wallet on a clean device.

  4. Remove persistence: delete the LaunchAgent, systemd unit, or C:\ProgramData\NodePackages staging, kill the detached process, and clear the dropper markers.

  5. Reimage where practical. For a RAT with established persistence, a clean rebuild is the safest outcome.

To reduce exposure to this class of attack going forward:

  • Disable install scripts by default with npm config set ignore-scripts true, and allow them only where a build genuinely needs them. This neutralizes postinstall droppers, and it is one of Snyk's npm security best practices.

  • Commit lockfiles and use npm ci in CI so builds are reproducible and a flipped latest tag cannot silently re-resolve.

  • Require provenance. Mastra generated SLSA provenance on CI publishes, but it was not required, so a plain token could still publish without attestations. The same gap lets malicious versions ship in the Nx package compromise; enforcing provenance on consumption raises the bar.

  • Watch for phantom dependencies. A mature, widely-used package suddenly gaining a single unused dependency is exactly the signal here. Risk-based prioritization and asset discovery across your environment help you identify where an affected package actually landed and which instances are reachable and credential-bearing, so you fix the exposed hosts first instead of chasing every install across the scope at once.

This is the latest in a run of npm scope and maintainer-account takeovers in 2026. The pattern, a compromised maintainer account driving a fast automated republish, is the same one behind the Mini Shai-Hulud campaigns: 

Mini Shai-Hulud: The Most Sophisticated NPM Supply Chain Attack of 2026 (a walkthrough of how a single compromised maintainer account is used to trojanize hundreds of npm packages at once, and how to detect and remediate exposure).

For more on the same attack family and defensive patterns, see Snyk's coverage of the Axios npm compromise, the Mini Shai-Hulud attack on AntV and on TanStack, the node-gyp self-propagating worm, and the tinycolor and ngx-bootstrap compromises. For a primer on hardening your install pipeline, see NPM security: preventing supply chain attacks, and the Snyk Learn lesson on the compromise of a legitimate package.

Timeline

  • Late 2024 to early 2025: the ehindero account publishes legitimate alpha releases of @mastra/core, then goes dormant. Scope access is never revoked.

  • June 16, 2026: a clean easy-day-js@1.11.21 is published as cover, impersonating dayjs.

  • June 17, 2026, ~01:01 UTC: the malicious easy-day-js@1.11.22 is published and tagged latest.

  • June 17, 2026, ~01:12 to 02:36 UTC: the bulk of the @mastra/* scope is republished with the injected easy-day-js dependency.

  • June 17, 2026: the incident is disclosed publicly by multiple security vendors; easy-day-js is removed from npm and the malicious versions are flagged (Snyk advisory SNYK-JS-EASYDAYJS-17353313).

  • June 17, 2026 (same day): Mastra responds, removing the unauthorized owner, opening emergency clean-publish changesets (PR #18049), forward-rolling all 142 publishable packages (PR #18056), and pinning explicit higher versions where the attacker had published above latest (PR #18060).

Secure your supply chain with Snyk

87% of our respondents were impacted by supply chain security issues. Keep yours secure with Snyk.