There is a malicious npm package that keeps its payload encrypted and stays dormant until a program does one particular piece of maths with it. The maths is the key.
The trigger
mathmain is a copy of mathjs under a different name. SafeDep found one added call near the end of the LU solver in the CommonJS build, whose return value is thrown away. That is the tell. The solver has already produced its answer, so the extra call exists for its side effects.
It leads to a function that turns its input into a JSON string and uses that string as a password. Scrypt turns it into a 256-bit key, and AES-256-GCM decrypts a filename, which the loader then decrypts, writes to disk and loads. Nothing in the visible code names the input that produces the password, and importing the library does not trigger any of it. The caller has to pass one specific 3 by 3 Pascal matrix to lusolve(), whose LU lower factor is the password.
JFrog found the trigger input; SafeDep reproduced it against the published files and read what came out. The package carries three encrypted blobs, the largest a 1.1 MB file under bignumber/. The decrypted code takes commands from an attacker and runs them, with the command channel going out through a public chat service and Sepolia smart contracts. Two deployments share an Alchemy project key, which ties them to one operator.
The part that matters
The same loader appears in mathsbase and math-universe, five versions across the three packages. It appears in neither public GitHub repository. The published tarball has the extra call in the solver; the source those packages link to does not. Someone added the loader when they pushed the package, so reading the repository tells you nothing about what you install.
The download counts you would use to size the problem cannot be used either. npm reported zero downloads across the whole registry for 17 September, which makes the 605,000, 1.9 million and 570,000 totals for these packages a poor measure of real installs. None of the three has public dependents.
My read
The shift worth noticing is that a reviewer reading the published code now sees nothing worth reading, because the payload only exists when the caller’s data turns into the key. Pin the tarball hash rather than a repository tag, because here they are different artefacts, and check what your dependency manager actually resolved rather than what the manifest says it asked for.
Sources: SafeDep, Why does an npm math library need an encrypted loader? and JFrog, The equation of compromise