Contents
ToggleExecutive Summary
On June 17, 2026, an attacker compromised the @mastra npm organization and mass-published 144 malicious package versions in an 88-minute window. The attack did not touch Mastra’s source code. The payload was hidden one level down, inside a dependency called easy-day-js — a typosquatted copy of the legitimate dayjs date library, engineered to survive casual code review by duplicating dayjs’s author name, homepage, repository URL, license, and version numbering verbatim.
@mastra/core alone receives over 918,000 weekly downloads. Mastra packages are routinely installed in environments that hold LLM API keys, cloud provider credentials, CI/CD tokens, and database connection strings. The second-stage payload is a cross-platform Node.js RAT that installs OS-level login persistence on Windows, macOS, and Linux, inventories 166 cryptocurrency wallet browser extensions, harvests browser history from Chrome, Brave, and Edge, and opens a remote module execution channel for arbitrary follow-on tasking.
The access vector was a dormant former Mastra contributor account (ehindero) whose scope permissions were never revoked. npm does not expire scope access on inactivity. A 16-month-old stale credential was sufficient to publish across the entire @mastra namespace. This campaign carries no CVE. No CVE-based scanner had surface coverage at the time of exploitation.
TL;DR for Engineering Teams
| What it is | Typosquatted npm dependency (easy-day-js@1.11.22) injected as a transitive dependency across 144 @mastra/* packages. Two-stage: obfuscated postinstall dropper + cross-platform Node.js RAT. No CVE assigned. |
| Where it bites | @mastra/* packages published on 2026-06-17 between 01:12-02:39 UTC. Any developer workstation, CI runner, or build environment that ran npm install in that window. Executes before application code is imported. |
| Why it matters | 918K+ weekly downloads on @mastra/core. Targets LLM API keys, cloud credentials, CI/CD secrets, and 166 cryptocurrency wallet extensions. Persistence survives npm uninstall. Zero CVE — traditional scanners are blind. |
| Patch status | No patch — remove affected versions entirely. Pin to last CI-published version with verified SLSA provenance attestations (the version immediately prior to each affected release). |
| Immediate action | 1. Run: npm ls easy-day-js across all repos and CI. 2. Treat any affected host as compromised. 3. Rotate LLM API keys, cloud credentials, npm tokens, CI/CD secrets. 4. Remove OS persistence artifacts (see IOC table). 5. Block 23[.]254[.]164[.]92 and 23[.]254[.]164[.]123 at egress. |
Vulnerability Overview
| Field | Value |
|---|---|
| Campaign Name | EASY_DAY_JS_MASTRA_2026 |
| Ecosystem | npm |
| Attack Type | Supply chain — typosquatted transitive dependency with postinstall hook |
| Malicious Package | easy-day-js@1.11.22 |
| Carrier Packages | 144 @mastra/* packages published 2026-06-17 |
| CWE | CWE-1395 (Dependency on Vulnerable Third-Party Component), CWE-506 (Embedded Malicious Code) |
| CVSS Score | N/A — no CVE assigned |
| CVE | None assigned |
| Patch Available | No — remove and pin to pre-incident versions with verified provenance |
| Active Exploitation | Confirmed — C2 infrastructure active at time of analysis (SafeDep) |
| Download Impact | 918K+ weekly (@mastra/core); >1.1M combined (StepSecurity) |
| Persistence | Windows Run key / macOS LaunchAgent / Linux systemd user unit — survives npm uninstall |
| Attribution | Unconfirmed; tradecraft similarities to Sapphire Sleet (BlueNoroff) noted — lower confidence |
Technical Analysis
The Typosquat: Why easy-day-js Survived Code Review

The choice of dayjs as the legitimate library to imitate was deliberate. dayjs is one of the most widely used JavaScript date utilities. Name recognition reduces the probability that a developer reviewing a dependency list flags easy-day-js as suspicious.
The attacker went beyond a name resemblance. Every field a developer would check in package.json was copied from the legitimate package:
| Attribute | Legitimate dayjs | Malicious easy-day-js |
|---|---|---|
| npm author | iamkun | iamkun (copied) |
| Homepage | https://day.js.org | https://day.js.org (copied) |
| Repository | github.com/iamkun/dayjs | github.com/iamkun/dayjs (copied) |
| License | MIT | MIT (copied) |
| Version series | 1.11.x | 1.11.21 → 1.11.22 (mirrored) |
| Keywords | dayjs, date, time, moment | dayjs, date, time, moment (copied) |
| npm maintainer | iamkun | sergey2016 (attacker) |
| Postinstall hook | None | node setup.cjs –no-warnings |
| setup.cjs | Not present | 4,572 bytes, obfuscated dropper |
The only reliable distinguishing signals are the npm maintainer account name and the presence of setup.cjs in the tarball. A routine npm audit or visual package.json review had no obvious flag. This is the attack surface that CVE-based tooling cannot see: a clean-looking dependency that is malicious only in its behavior at install time.
Pre-positioning: Clean Bait Version (June 16)
The attack started the day before the Mastra compromise. On June 16 at 07:05 UTC, account sergey2016 published easy-day-js@1.11.21 — a complete, functional copy of dayjs with no malicious code and no install hooks. Its only function was to establish a credible package history.
The dependency was then injected into Mastra packages as:
“easy-day-js”: “^1.11.21”
The caret range is the delivery mechanism. npm resolves to the highest compatible version at install time. Developers auditing the pinned clean bait version in a lockfile see a harmless date library. The payload rides in on the version npm actually installs.
Account Takeover and Scope Abuse (June 17, 01:01–01:12 UTC)
At 01:01 UTC, easy-day-js@1.11.22 went live with the obfuscated setup.cjs dropper. Eleven minutes later, the ehindero account began the mass publish.
The ehindero account was a legitimate former contributor who published 15 alpha versions of @mastra/core between November 2024 and February 2025 before going dormant for 16 months. The June 17 publishes came from the same account name but with a switched email (tutamail.com vs the original gmail.com), matching the sergey2016@tutamail.com pattern from the easy-day-js publisher. This is consistent with account takeover followed by email substitution on a dormant credential.
npm does not expire scope permissions on inactivity. One stale maintainer token was sufficient to publish across the entire @mastra namespace. The provenance fingerprint made this immediately visible to anyone checking:
@mastra/core@1.42.0 publisher: npm-oidc-no-reply@github.com provenance: yes
@mastra/core@1.42.1 publisher: ehindero2016@tutamail.com provenance: no
This gap repeats across all 144 packages. A policy requiring SLSA attestations on install would have rejected every package in this wave.
Stage 1: Obfuscated Postinstall Dropper (setup.cjs)
The dropper is 4,572 bytes, obfuscated with three stacked layers:
- Custom-alphabet Base64: String literals use a shuffled alphabet. Standard decoders produce garbage without reversing the mapping.
- Array rotation with integrity check: A 40-element string array must be rotated exactly 34 positions before an arithmetic checksum equals 0x4c11d. Static analysis tools evaluating the array pre-rotation index the wrong strings.
- XOR-encoded beacon marker: The string easy-day-js is stored as raw bytes [0xe5, 0xe1, 0xf3, 0xf9, 0xad, 0xe4, 0xe1, 0xf9, 0xad, 0xea, 0xf3] (XOR 0x80). The plaintext string never appears in any file written to disk.
The deobfuscated execution flow:
- Set NODE_TLS_REJECT_UNAUTHORIZED=0 — disables certificate validation for the C2 fetch
- Write install path to <tmpdir>/.pkg_history (victim beacon)
- Write XOR-encoded package marker to <tmpdir>/.pkg_logs
- Fetch stage-2 payload from hxxps://23[.]254[.]164[.]92:8000/update/49890878
- Write payload to <tmpdir>/<24-hex-chars>.js (random filename)
- Spawn payload as detached background process; pass 23[.]254[.]164[.]123:443 as the RAT’s C2 argument
- Delete setup.cjs from disk (fs.rmSync __filename)
The process continues running after npm install exits. Inspecting node_modules/easy-day-js after execution finds no setup.cjs.
Stage 2: Cross-Platform Node.js RAT
The downloaded second stage is approximately 41 KB — a cross-platform Node.js tasking client, not a fire-and-forget stealer.
Reconnaissance on first beacon includes: username, hostname, OS and architecture, Node.js version, installed application inventory, running process list, browser history hostnames, and cryptocurrency wallet extension inventory (166 wallet IDs matched against Chrome, Brave, and Edge profiles).
After the initial beacon, the RAT enters a polling loop checking for operator commands. The primary command path downloads an arbitrary module from an attacker-supplied URL and executes it (Node runner or shell runner). This means credential theft, lateral movement, or any other capability can be delivered after initial infection without any changes to the installed package.
Persistence is installed under Node/NVM-themed names on all three platforms:
| Platform | Mechanism | Drop Path |
|---|---|---|
| Windows | HKCU Run key (NvmProtocal) — hidden PowerShell | C:\ProgramData\NodePackages\protocal.cjs |
| macOS | LaunchAgent com.nvm.protocal.plist (RunAtLoad) | ~/Library/NodePackages/protocal.cjs |
| Linux | systemd user unit nvmconf.service (ExecStart) | ~/.config/systemd/nvmconf/protocal.cjs |
On macOS, signal and exit handlers re-invoke the persistence function — killing the process can install persistence if it has not already been written. Removing node_modules does not remove the persisted RAT.
Affected Versions
All 144 @mastra/* package versions published on 2026-06-17 between 01:01-02:39 UTC. Representative high-impact packages:
| Package | Compromised Version | Last Clean Version | Notes |
|---|---|---|---|
| easy-day-js | 1.11.22 | Do not use | Primary dropper — remove entirely |
| @mastra/core | 1.42.1 | 1.42.0 | 918K+ weekly downloads |
| @mastra/memory | 1.20.4 | 1.20.3 | |
| @mastra/server | 2.1.1 | 2.1.0 | |
| @mastra/mcp | 1.10.1 | 1.10.0 | |
| @mastra/deployer | 1.42.1 | 1.42.0 | |
| mastra | 1.13.1 | 1.13.0 | Root package |
| create-mastra | 1.13.1 | 1.13.0 | Project scaffolding |
| @mastra/rag | 2.2.2 | 2.2.1 | |
| @mastra/schema-compat | 1.2.12 | 1.2.11 | First package compromised (01:12 UTC) |
Full IOC list of all 144 affected packages is in the campaign-config.json and the campaign-details.md. Safe versions are the last GitHub Actions-published release prior to the incident window, identifiable by SLSA provenance attestation on the npm registry.

Exposure Analysis
| Environment | Risk Level | Reason |
|---|---|---|
| Developer workstations (AI/LLM tooling) | Critical | Primary target; hold LLM API keys, cloud credentials, wallet browser extensions |
| CI/CD runners | Critical | Automated installs; credentials in env; artifacts from infected runners are suspect |
| Cloud workloads built from affected CI | High | May contain persisted RAT in base images or deployment artifacts |
| Internet-facing services | Medium | Indirect exposure via compromised deployment pipelines |
The Mastra ecosystem sits at the intersection of AI development and cloud infrastructure (StepSecurity). Its packages are installed in environments that hold LLM API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY), cloud provider credentials, database connection strings, CI/CD tokens, and cryptocurrency wallet browser extensions — the highest-value credential classes in modern AI development pipelines.
Real-World Impact
The combined weekly download exposure across affected packages exceeds 1.1 million installs per week (StepSecurity). @mastra/core alone receives over 918,000. The 88-minute publish window affected the entire @mastra namespace with an automated publishing campaign, not manual package-by-package modification.
Socket flagged the malicious easy-day-js within six minutes of publication. JFrog Curation customers using an immaturity policy were protected within 24 hours. StepSecurity Harden-Runner blocked the outbound C2 call during a controlled analysis run before any stage-2 payload downloaded. Despite this rapid detection by behavioral tools, any organization that ran npm install in the incident window before detection was exposed.
Zero-CVE structural gap: This campaign carries no CVE. No CVE-based scanner had any detection surface during active exploitation. The Phoenix Malware Package Intelligence corpus now tracks 60 campaigns across June 2024 to June 2026. Zero CVEs were assigned during active exploitation in any of these campaigns. CVE-based detection does not cover this threat class.
Detection Guidance
Log Indicators
- Outbound HTTPS connections: From Node.js processes to 23.254.164.92:8000 or 23.254.164.123:443 during npm install steps
- User-Agent anomaly: Node.js processes using mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)
- TLS certificate: wolfSSL test certificate (CN=www.wolfssl.com, expired Jan 2018) on any HTTPS connection
- Environment variable: NODE_TLS_REJECT_UNAUTHORIZED=0 set in npm install subprocesses
- File artifacts: .pkg_history and .pkg_logs in the OS temp directory; randomly-named <24-hex-chars>.js files in tmpdir
- Process anomaly: Detached Node.js processes spawned from os.tmpdir() or NodePackages directories
Scanner References
- JFrog Xray / Curation: Detects easy-day-js@1.11.22 and all affected @mastra packages (XRAY-1004962)
- Socket Security: Flagged malicious easy-day-js within 6 minutes of publication; blocks on install
- StepSecurity Harden-Runner: Blocks outbound connections to C2 IPs in CI/CD; blocks at network layer before payload downloads
- SafeDep Secure Registry: Package cooldown policy blocks recently published versions before serving to CI/CD
- Phoenix Blue Shield CI/CD Firewall: Behavioral install-time detection; PHX-Neural scoring surface for postinstall hook anomalies
- Phoenix SCA scanner integration: Campaign IOC matching against package.json and lockfile manifests; full 144-package IOC list in campaign-config.json
- phxintel.security: Live malware feed at phxintel.security/malware.html tracks this campaign
Verification Steps for Teams
- Run npm ls easy-day-js in all repositories and CI workspaces to identify transitive installation
- Search lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) and SBOMs for easy-day-js at any version
- Review npm audit signatures output for @mastra/* packages installed on 2026-06-17 — missing provenance attestations indicate the malicious wave
- Search for persistence artifacts on potentially affected hosts (file paths in IOC table above)
- Check CI logs for outbound connections to 23.254.164.0/24 during npm install steps
- Search for detached Node.js processes running from tmpdir or NodePackages paths
Remediation Guidance
Immediate Actions
- Remove all @mastra/* versions published on 2026-06-17 and easy-day-js from dependency manifests and lockfiles
- Pin to the last clean pre-incident version with verified SLSA provenance attestations (one patch version below each affected release)
- Clear local and CI package caches where malicious tarballs may persist; prefer rebuilding CI runners from clean base images over reusing existing workspaces
- Rotate all credentials accessible to affected environments: npm tokens, GitHub tokens, cloud provider credentials (AWS/Azure/GCP), LLM API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY), database connection strings, CI/CD secrets
- If cryptocurrency wallet extensions were present in browsers on affected machines, migrate wallet funds to new seed phrases generated on a clean device — password rotation alone is not sufficient
- Block 23.254.164.92 and 23.254.164.123 at network egress
Persistence Removal
Removing node_modules is not sufficient. Remove OS-level persistence artifacts on all potentially affected hosts:
- macOS: launchctl unload ~/Library/LaunchAgents/com.nvm.protocal.plist && rm ~/Library/LaunchAgents/com.nvm.protocal.plist && rm -rf ~/Library/NodePackages/
- Linux: systemctl –user disable nvmconf.service && systemctl –user stop nvmconf.service && rm ~/.config/systemd/user/nvmconf.service && rm -rf ~/.config/systemd/nvmconf/ && rm -rf ~/.config/NodePackages/
- Windows: Remove HKCU\…\CurrentVersion\Run\NvmProtocal registry value; delete C:\ProgramData\NodePackages\
CI/CD Hardening
- Enforce npm audit signatures or equivalent provenance attestation policy to reject packages published without SLSA provenance
- Use npm ci instead of npm install in CI pipelines to enforce lockfile consistency
- Run npm install –ignore-scripts where no first-party dependency requires lifecycle scripts; allowlist specific packages that legitimately need postinstall
- Add network egress controls to CI runners: alert on outbound connections to raw IPv4 literals during dependency installation steps
- Implement package cooldown periods: delay adoption of package versions published within the last 24-48 hours
Phoenix Security Platform Recommendations
This campaign illustrates the structural detection failure that Phoenix Security’s platform is built to address. Traditional SCA scanners check package names and CVE databases. This attack has no CVE. The zero-CVE gap is not an edge case — it is the consistent pattern across all 60 campaigns in the Phoenix MPI corpus.
| Capability | Application to This Campaign |
|---|---|
| Campaign Builder | Import the easy-day-js campaign IOC list (144 packages, full version table) and scan all repositories against it automatically |
| Blue Shield CI/CD Firewall | Intercept postinstall hooks and behavioral anomalies at install time; block outbound C2 connections from npm install processes |
| PHX-Neural Behavioral Scoring | Score postinstall hook behavior, TLS bypass patterns, and detached process spawning — signals that carry no CVE but indicate active compromise |
| Reachability Analysis | Identify which projects transitively resolve easy-day-js via @mastra/* package dependencies |
| Attack Surface Management | Identify internet-facing services built from CI runners that ran in the incident window |
| Remediation Campaigns | Create campaign → assign owners to each affected repository → track remediation status → verify lockfile cleanup across the estate |
Download campaign-config — full 144-package IOC list, network indicators, and SHA256 hashes for direct import into Phoenix Campaign Builder.
Phoenix Security correlates vulnerable components with runtime workloads, identifies exposed services via attack surface management, and assigns remediation ownership — turning a 144-package incident into a tracked, owned remediation backlog rather than an open-ended manual triage.

Detection Community: Research Sources
This campaign was analyzed by four independent research teams whose combined work produced the full technical picture within hours of the attack window:
JFrog Security Research
JFrog performed complete static analysis of both stages, including deobfuscated loader logic, C2 protocol reconstruction, and persistence mechanism documentation. JFrog Xray has assigned XRAY-1004962 to easy-day-js and all 144 affected Mastra packages. JFrog Curation customers using an immaturity policy were protected within 24 hours. The full IOC table and loader code reconstruction in this report draw from JFrog’s technical analysis.
Reference: https://research.jfrog.com/post/easy-day-js/
SafeDep
SafeDep identified the account takeover mechanism, documented the SLSA provenance gap as a detection fingerprint, and performed live C2 analysis confirming the stage-2 RAT was still accepting beacons at time of publication. SafeDep’s analysis noted tradecraft similarities to the Axios npm compromise attributed to Sapphire Sleet (BlueNoroff) earlier in 2026 — attribution is not confirmed for this incident but the overlap is close. SafeDep’s Secure Registry product blocked affected packages via cooldown policy before they reached customer CI pipelines.
Reference: https://safedep.io/mastra-npm-scope-takeover-supply-chain-attack/
Socket Security
Socket flagged the malicious easy-day-js within six minutes of publication. Socket’s analysis documented the full 141-package affected list, the complete stage-2 implant capabilities including the ICAP-style exfiltration protocol and the 166-wallet extension targeting list, and confirmed that the identical loader sample appeared on public sandboxes on 2026-05-29 — 19 days before the Mastra publish, indicating reused tooling rather than a purpose-built attack. Socket customers received automatic blocking before any malicious install hook could execute.
Reference: https://socket.dev/blog/mastra-npm-packages-compromised
StepSecurity
StepSecurity performed runtime analysis of the attack chain via Harden-Runner in block mode, validating that blocking the outbound connection to 23.254.164.92:8000 prevented stage-2 payload download entirely. StepSecurity documented the three-layer obfuscation scheme including the custom-alphabet Base64, array rotation integrity check, and XOR-encoded beacon marker. The C2 IP 23.254.164.92 was added to the StepSecurity Harden-Runner Global Block List immediately. StepSecurity also noted the package cadence was consistent with an automated publishing script processing the @mastra namespace in batches — not manual package-by-package modification.
Reference: https://www.stepsecurity.io/blog/mastra-npm-packages-compromised-using-easy-day-js
External References
- The Hacker News — 144 Mastra npm Packages Compromised: https://thehackernews.com/2026/06/144-mastra-npm-packages-compromised-via.html
- JFrog Security Research — easy-day-js: Supply Chain Campaign Targets Mastra npm Packages: https://research.jfrog.com/post/easy-day-js/
- SafeDep — Mastra npm Scope Takeover: 141 Packages Drop a RAT: https://safedep.io/mastra-npm-scope-takeover-supply-chain-attack/
- Socket Security — 140+ Mastra npm Packages Compromised in Coordinated Supply Chain Attack: https://socket.dev/blog/mastra-npm-packages-compromised
- StepSecurity — Mastra npm Supply Chain Attack: 140+ Packages Backdoored via easy-day-js Typosquat: https://www.stepsecurity.io/blog/mastra-npm-packages-compromised-using-easy-day-js
- GitHub Disclosure Issue — mastra-ai/mastra#18045: https://github.com/mastra-ai/mastra/issues/18045
- npm Advisory — Mastra Incident: https://npmjs.com (search @mastra compromise June 2026)
- Phoenix Security phxintel.security malware feed: https://phxintel.security/malware.html
- JFrog Catalog campaign label — easy-day-js: https://catalog.jfrog.io (search easy-day-js)
- StepSecurity Harden-Runner controlled run evidence: https://app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/27661280135