Teardown: the Mastra npm compromise

One phished maintainer, 140+ packages, one new dependency.

What happened on 17 June 2026, how a stolen npm token got past MFA, and where Chainsaw would and would not have stopped it.

Questions and answers

What happened?

On 17 June 2026 (UTC), more than 140 packages of the Mastra AI framework on npm got a new patch version with one added dependency, easy-day-js, whose install script downloaded and ran a second-stage payload. @mastra/core alone gets about 918,000 downloads a week, according to Socket, and Microsoft reports the poisoned versions were tagged latest.

The count and the window depend on who you read. Mastra’s incident report says a token published 116 malicious packages between 6:12 and 6:37 PM Pacific on 16 June, which is 01:12 to 01:37 UTC on 17 June. StepSecurity tracked republishes from 01:12 to 02:39 UTC, Socket counts 141 @mastra/* packages, and isotope¹³ notes that reported counts range from 140 to 145.

Mastra says it noticed at 8:45 PM Pacific and by 11:57 PM had unpublished 110 of the 116 packages and deprecated the other six, which npm would not let it unpublish. It then published new, safe versions of each affected package. GitHub and OSV listed @mastra/core@1.42.1 as malware (GHSA-pp62-grrw-hvfp, MAL-2026-6011), and OSV listed easy-day-js@1.11.22 as MAL-2026-5979 at 04:15 UTC, about three hours after it went up. On 19 June Microsoft attributed the operation with high confidence to Sapphire Sleet, a North Korean state actor focused on stealing cryptocurrency.

How did the attacker get publish rights?

By phishing a Mastra maintainer and using a token tied to his npm account. Mastra’s report says a compromised LinkedIn account contacted him and maintainers of other prominent TypeScript packages; during a call he clicked a suspicious link, and his machine was compromised. Mastra required MFA for npm maintainers but, in its words, mistakenly allowed token bypass, so the token could publish without a second factor. Mastra removed the bypass across all its packages.

Sources disagree about the account. Mastra says the maintainer is a current, active employee. Snyk and isotope¹³ describe the account as a former contributor’s whose publish access was never revoked, and Snyk says it had been dormant since early 2025.

The publish path changed as well. Microsoft and Socket both report that earlier Mastra releases were published by the project’s GitHub Actions pipeline, while the malicious wave came from a single person’s npm account outside it.

What changed in the packages?

One line in package.json. Socket found each malicious release byte-identical to the last legitimate build apart from a patch version bump and manifest normalization, plus one new dependency: "easy-day-js": "^1.11.21". Microsoft found no matching change in Mastra’s GitHub repository, and Snyk notes the dependency is never imported. It is there only to be installed.

easy-day-js is a lookalike of the dayjs date library, with dayjs’s author name and links copied into its manifest. The npm registry shows its first version, a clean 1.11.21, published at 07:05 UTC on 16 June by an account the researchers name as sergey2016. Version 1.11.22, which added the install hook, followed at 01:01 UTC on 17 June, about 11 minutes before the first Mastra republish. Because of the caret range, a fresh install of any affected Mastra package resolved to 1.11.22. When the Mastra packages started pulling it in, the easy-day-js name had existed for about 18 hours.

What ran on install?

In the June 2026 Mastra npm compromise, the dropper easy-day-js@1.11.22 ran node setup.cjs --no-warnings as a postinstall hook. The 4,572-byte setup.cjs is an obfuscated loader, and according to Microsoft and StepSecurity the script:

  • set NODE_TLS_REJECT_UNAUTHORIZED=0, turning off certificate checks for its requests;
  • wrote .pkg_history (the install path) and .pkg_logs (an encoded package-name marker) to the temp directory;
  • downloaded a second stage from https://23.254.164[.]92:8000/update/49890878;
  • saved it as a randomly named .js file, in the temp directory according to StepSecurity and the home directory according to Microsoft, and ran the file with Node.js as a detached, hidden process;
  • deleted setup.cjs, so the package directory no longer showed the dropper.

Microsoft and Socket analysed the second stage, a Node.js tasking client of about 41 KB which reports to 23.254.164[.]123 and runs whatever code the operator sends back. The client persists under names resembling Node tooling: a Run key value NvmProtocal on Windows, a LaunchAgent com.nvm.protocal on macOS, and a systemd user unit nvmconf.service on Linux. The client checks installed browsers for 166 cryptocurrency wallet extensions, reads Chrome, Edge and Brave history, and collects host details, installed applications and running processes.

The sources differ on what the client took. Mastra’s report describes a script which exfiltrated credentials. Socket says its recovered sample does not read saved passwords or cookies or copy wallet data, and StepSecurity did not observe the second stage at all because its own tooling blocked the download. Per Microsoft, on hosts where the implant reached its server, the operator delivered more tooling: a PowerShell backdoor from teams.onweblive[.]org beaconing to maskasd[.]com, a Microsoft Defender exclusion, and a Windows service named scdev.

Would Chainsaw have stopped it?

Yes, on day 0, for installs that go through the Chainsaw proxy. We measured this in our re-run of 58 attacks from 2026 against production Chainsaw (v0.22.118) on 19 October 2026. No policy change is needed: both rules below are on by default.

  • The dropper is refused on first download. A default rule, ‘Block packages released within 1 day’, refuses packages that first appeared on npm recently. easy-day-js is about 18 hours old when the Mastra packages start pulling it, so the install fails before setup.cjs can run, with no malware feed entry needed.
  • The malicious Mastra versions are held back. Chainsaw holds every new version for 48 hours by default, so npm install @mastra/core through the proxy resolves to 1.42.0. The known-malicious feed lists @mastra/schema-compat@1.2.12, easy-day-js@1.11.22 and @mastra/core@1.42.1 2 to 4 hours after publish, so they stay refused once the hold ends.
  • The age rule has a window. It stops matching easy-day-js about 48 hours after the package first appeared. From then on, easy-day-js@1.11.22 is refused by its feed listing, not its age.
  • The install script alone is not a reliable stop. The byte scan escalates an obfuscated loader like setup.cjs only above a threshold of obfuscated identifiers, and a readable loader that downloads a payload and spawns it with Node is not refused on its contents. The age rule and the hold are what stop this one.
  • Installs that bypass the proxy get the malware. A machine or CI runner pointed straight at registry.npmjs.org is not protected.

What should you do?

Every install of one of the more than 140 Mastra packages republished on 17 June 2026, for example @mastra/core@1.42.1, also pulled in easy-day-js@1.11.22 and ran its postinstall dropper. First find out whether easy-day-js was ever installed. Run npm ls easy-day-js and npm ls @mastra/core mastra create-mastra, search lockfiles and CI logs for easy-day-js, and look for .pkg_history and .pkg_logs in the temp directory.

If easy-day-js was installed:

  1. Treat the machine or runner as compromised. GitHub’s advisory warns that removing the package may not remove everything the dropper installed.
  2. Rotate secrets from a different computer: npm and GitHub tokens, cloud keys, SSH keys, CI/CD secrets and LLM API keys the host could reach.
  3. Remove the persistence and kill the detached process. Socket notes uninstalling the package is not enough: look for the NvmProtocal Run value, the com.nvm.protocal LaunchAgent and the nvmconf.service unit.
  4. Install a clean Mastra release and regenerate the lockfile so easy-day-js is gone. Snyk advises upgrading to a clean release rather than pinning down; Microsoft points to the last release before the attack, such as @mastra/core@1.42.0, and Socket to the last release published by GitHub Actions, after checking its provenance.
  5. Move cryptocurrency out of browser-extension wallets on the affected machine to a new wallet created on a clean device, as Socket and Snyk advise.
  6. Search network logs for 23.254.164[.]92 and 23.254.164[.]123, and block them, along with teams.onweblive[.]org and maskasd[.]com.

For the next one:

  1. Don’t install brand-new packages or versions straight away. Socket and StepSecurity both list release cooldowns as a defence. easy-day-js@1.11.22 was 11 minutes old when the first Mastra package started depending on the dropper.
  2. Turn off install scripts by default. Snyk suggests npm config set ignore-scripts true, Microsoft --ignore-scripts; Socket suggests allowlisting the packages that need them.
  3. Watch for new dependencies in mature packages and for releases that skip the usual pipeline. Snyk recommends requiring npm provenance on the packages you consume.
  4. If you publish packages, don’t let tokens bypass MFA, and remove publish access people no longer need. Those are the two account-level fixes Mastra and isotope¹³ describe.

Sources

One teardown per notable attack

Get the next teardown by email

What happened, the install-path angle most coverage misses, and the rule that refuses it.