Safeguard
Supply Chain Security•

easy-day-js: a dayjs typosquat injected into 140 Mastra packages

On 17 June 2026 an attacker added a lookalike of dayjs as a dependency across 140+ @mastra packages. The dropper was blocked in one test by an egress rule.

Safeguard Security Team
4 min read

On 17 June 2026 an attacker who had taken over the @mastra npm organization republished more than 140 packages with one extra dependency: easy-day-js, a typosquat of the popular dayjs date library. StepSecurity put the combined exposure at more than 1.1 million weekly downloads and advised that anyone who installed a @mastra package that day should treat the environment as compromised.

This post looks at the dependency trick itself, which is different from the account takeover angle. The malicious code was never in the Mastra packages. It arrived one hop away.

What's affected

  • easy-day-js@1.11.22: the malicious release. 1.11.21, published the day before by npm user sergey2016, was a clean copy of dayjs used as bait.
  • Compromised @mastra releases published on 17 June, starting with @mastra/schema-compat@1.2.12 at 01:12 UTC, then @mastra/core@1.42.1, @mastra/memory@1.20.4, @mastra/server@2.1.1, mastra@1.13.1 and many more through 02:39 UTC.

The attacker copied dayjs metadata: author, homepage, repository, license and keywords. Two things gave it away: the maintainer account and a setup.cjs file in the tarball.

How it worked

Each compromised package listed easy-day-js as a production dependency with the range ^1.11.21. The caret range meant npm resolved the newest match, 1.11.22, which ran a postinstall hook: node setup.cjs --no-warnings.

The dropper turned off TLS verification, wrote marker files to the temp directory, downloaded a second stage from a hard-coded address at runtime, ran it detached, then deleted setup.cjs. The second stage was not in any tarball. StepSecurity infers credential theft but says it is not confirmed.

How to check if you're exposed

npm ls easy-day-js
grep -n 'easy-day-js' package-lock.json pnpm-lock.yaml yarn.lock
ls -la "${TMPDIR:-/tmp}"/.pkg_history "${TMPDIR:-/tmp}"/.pkg_logs 2>/dev/null

Also check network logs for connections to 23.254.164.92 on port 8000 and 23.254.164.123 on port 443.

What to do now

  1. Remove easy-day-js and any @mastra version published on 17 June, then reinstall from a clean state.
  2. If the marker files exist, treat the host as compromised and rotate environment secrets, tokens and cloud keys it could read.
  3. Block the two IP addresses above at your egress layer.
  4. Search other lockfiles for easy-day-js before closing the incident.

How this could have been avoided

  • Review new dependencies in dependency diffs. A patch release of a core package that suddenly gains a date library nobody asked for is easy to spot in a lockfile diff.
  • Block outbound connections from install steps. StepSecurity ran @mastra/core@1.42.1 in a GitHub Actions job with Harden Runner in block mode. The call to the stage-2 server was blocked, so the payload never ran.
  • Use a dependency cooldown. easy-day-js@1.11.22 was published about 18 hours after the bait version and minutes before the first Mastra release.
  • Use exact pins and lockfiles in CI with npm ci, so a caret range cannot pull in a version that did not exist when you last reviewed.
  • Check publisher identity. The lookalike came from a different maintainer than dayjs.

Why it matters

Scanning for known malware in direct dependencies would not have helped here. The trusted package was malicious by a single new line in its dependency list.

Typosquats are usually a user error. This one was a delivery mechanism, and the user did nothing wrong except update.

Try it yourself

Take one project and run npm ls --all | wc -l to count the packages you actually install. Then diff the dependency tree between your last two lockfile commits. Every new name that appeared in a patch update deserves a look.

Click through it at your own pace, or open it full screen.

Quick check

Why did npm install the malicious easy-day-js version automatically?

The Mastra packages declared it with a caret range, so npm resolved to the newest matching release, 1.11.22.

What stopped the payload in StepSecurity's test?

An outbound allowlist in block mode, which refused the connection to the second-stage server.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.