# GitHub Had the Math. npm Had the Encrypted Loader.

> SafeDep found a remote-access implant in npm mathjs clones. The loader stays dark until a caller hits the solver with matrix data as the password. JFrog recovered that trigger on 21 September. Reviewed GitHub trees lack the extra call. The npm tarball does. npm later pulled the packages. Download counts were not a user base.

Source: https://hackerlogs.com/blog/mathmain-encrypted-loader
Published: 2026-09-22

## Key takeaways

- Hacker News put SafeDep on the board today. JFrog published the trigger on 21 September. The implant waits in lusolve after the honest answer is already computed.
- No install hook. A normal import does not wake it. The password is not in the package. The caller's own matrix data has to produce it.
- Reviewed GitHub commits for mathsbase and math-universe lack the extra solver call. The npm build has it. Source is not the artifact.
- Three names: mathmain, mathsbase, math-universe. npm download totals in the hundreds of thousands do not match dependents. JFrog calls the traffic a farm.
- Hunt the tarball hashes and a LICENSE line that does not belong. A pulled package is not a hunt of every lockfile that ever resolved it.

Hacker News put [SafeDep's mathmain post](https://safedep.io/mathmain-encrypted-loader/) on the front page today. US X Trends were football. Google still had yesterday's Codex escapes. This is the other ticket: an npm math clone whose solver waits for one piece of matrix data, then unlocks a remote-access implant.

 Encrypted RAT in a mathjs copy. High because the GitHub tree was not the artifact, not because a named victim posted a dump.

<img src="/mathmain-encrypted-loader.svg" alt="mathmain encrypted loader: a solver that stays dark until the right matrix data lands, a clean GitHub tree against a dirty npm tarball, and the operator work of hunting lockfiles after npm pulled the name." width="1200" height="675" />

## What SafeDep and JFrog actually published.

SafeDep started on 17 September with `mathmain`, a renamed [mathjs](https://www.npmjs.com/package/mathjs) tree plus obfuscation. One extra call sat at the end of the linear solver, after the honest result already existed. That call handed matrix data to a helper that treated the JSON form of the lower factor as a password, then tried to decrypt and load staged files.

[JFrog](https://research.jfrog.com/post/equation-of-compromise/) published the trigger on 21 September. SafeDep reproduced it the same day. The implant does not ship the password. The caller's own numerical work has to produce it. Any other input fails the authentication check. The solver still returns the correct answer. The extra call's return value is thrown away.

The same loader family landed in `mathsbase` and `math-universe`. SafeDep hashed five tarballs. On 17 September, npm's default `mathmain` release was a version without this loader. Checking only latest would have missed it.

We are not reprinting the decrypt steps or the exact matrix as a working recipe. The operator fact is enough. A math import can hide a staged implant that only a later call unlocks.

> SafeDep read the public trees for mathsbase and math-universe. The extra solver call is not there. The npm tarball has it. JFrog's campaign write-up starts from the same mismatch. Source is a marketing page. The artifact is what Node loads.

Hacker News commenters saw npm's removal notice. Treat that as the registry clock, not the impact clock. Same shape as the [TanStack token](/blog/tanstack-token-lasted-months): the package can be gone while a lockfile still points at the tarball.

## Why the GitHub tree is the wrong receipt.

JFrog's download picture does not match dependents. mathsbase showed millions of downloads days after publish, and no package depending on it. SafeDep's week of counts for the three names sat in the hundreds of thousands, then npm reported a zero-download day for the whole registry. Neither number is an install base. JFrog describes a farm of GitHub Actions workers manufacturing traffic.

The implant, once unlocked, is a small remote-access stack. SafeDep's static read: host inventory, a testnet contract as a drop, Slack and Telegram as side channels, later stages written to disk and started. JFrog adds an infection marker hidden in LICENSE, a detached respawn, and a wallet that only unwraps on a host that both installed the package and hit the trigger. Very Serious Systems independently decrypted a stage and found a broken bootstrap on one copy. Do not file every host as fully tasked. File the class: encrypted payload, runtime key, command channel that looks like ordinary Web3.

> A yanked npm name is not a hunt. Search lockfiles, caches, and images for the hashed versions. A clean GitHub commit is not a tarball review. A huge download count is not users.

## What this is not.

It is not [TanStack](/blog/tanstack-token-lasted-months) and not [Plugin4Shell](/blog/plugin4shell-sha-pin-rce). Those were a trusted publisher and a pin that did not verify. This is a clone that waited for a solver call.

It is not a drive-by install worm. SafeDep's path needs the extra call and the right matrix data. That is why public searches found no caller. Private code is still in scope.

It is also not a reason to trust latest. The clean default and the dirty version sat on the same name.

## What to do.

If you install npm math helpers, or your agents do:

1. Read SafeDep and JFrog as the scope. Three names. Loader in the tarball, not in the reviewed GitHub tree. Trigger after an honest solve. npm pulled the malicious code.
2. Search lockfiles and images for mathmain, mathsbase, and math-universe. Match SafeDep's archive hashes, not the live page.
3. Compare any mathjs clone's GitHub tree to the tarball you actually installed. The extra solver call is the tell.
4. Hunt the LICENSE marker and decrypted stage files JFrog and SafeDep named. A yanked package can still have written them.
5. Block the family at install. Do not use download counts as popularity. Do not let an agent add a math helper because the README looks like mathjs.

The package clock is a removal notice. The impact clock is every cache that still has the tarball. GitHub was the brochure. npm was the implant.

## Sources

- [Why Does an npm Math Library Need an Encrypted Loader?](https://safedep.io/mathmain-encrypted-loader/) (2026-09-18)
- [Equation of Compromise: Anatomy of a Live npm Supply-Chain Campaign](https://research.jfrog.com/post/equation-of-compromise/) (2026-09-21)
- [Why does mathmain need an encrypted loader?](https://news.ycombinator.com/item?id=49791378) (2026-09-22)
- [lusolve() and you shall receive](https://research.veryserious.systems/lusolve-and-you-shall-receive/) (2026-09-21)
