Research

148 fake Tailwind CSS plugins on npm that run code when your build loads them

148 npm packages pose as Tailwind CSS or PostCSS plugins. When a build loads one from tailwind.config.js or postcss.config.js, it runs hidden code in Node with the developer's permissions. Some find their server through the Ethereum blockchain. Others keep the payload in a private package that only a fake job interview can install.

Published LPM Firewall research
packages
148
versions
333
server address on Ethereum
45
fake job interview lures
28
LPM confirmed
35

Summary

  • 148 packages and 333 versions. 131 of them were first published between May and October 2026.
  • They run when the build loads the plugin or a module imports it. Only 2 of the 125 whose method is known use an install script, so npm install --ignore-scripts does not stop them.
  • 45 read the address of their server from transactions on the Ethereum blockchain. The same loader is in 27 more packages with other disguises.
  • 28 belong to fake job interviews. The public plugin is clean, and the payload sits in a private npm package that installs only with a token committed to the interview repository.
  • 31 download code from a fixed server or paste service, and 20 carry an encoded payload inside the package.
  • LPM's own review confirms 35 as malicious, and 146 have an OpenSSF/OSV malicious-package advisory. LPM found 2 that have no advisory.

Why a Tailwind plugin

Tailwind and PostCSS configs are JavaScript. Every plugin listed in them is loaded with require each time npm run dev or npm run build starts, in a Node process that can read environment variables and files and open network connections. Code in the plugin runs then, long after install, on developer machines and in CI.

Most of these packages keep a working plugin in front. They copy the code and README of @tailwindcss/forms, @tailwindcss/typography, tailwindcss-animate or tailwindcss-3d, so the styles still work. 20 imitate @tailwindcss/forms alone. The malicious part is appended after the export, often behind a long run of whitespace that pushes it off-screen in an editor.

How the payload arrives
  • Server address read from the Ethereum blockchain45
  • Code downloaded from a fixed server or paste service31
  • Payload in a private package (fake job interviews, with the helper)29
  • Encoded payload inside the package20
  • Advisory only, method not published23
New fake plugins per month2026
  • May: 4
  • Jun: 16
  • Jul: 24
  • Aug: 30
  • Sep: 49
  • Oct: 8

A server address on the blockchain

  1. On import, an obfuscated block runs, either eval(atob("…")) or an obfuscator.io bundle. It saves require and module as globals for the next stage.
  2. It asks public Ethereum RPC and explorer services, such as drpc.org, 1rpc.io, publicnode.com and blockscout, for the latest transaction sent from one fixed address.
  3. It decodes two IPv4 addresses from the recipient field of that transaction.
  4. It downloads XOR-encrypted JavaScript from /0x/cls and /0x/ls on port 443 of those addresses, over plain HTTP, and runs it with eval and in a detached Node process.

The package never names its server. To move it, the operator sends a new transaction, and every installed copy follows. Nothing on npm changes, and a blockchain record cannot be taken down. As of Nov 3, 2026, the sender address had made 609 transactions.

The same loader, address included, also appears in 27 packages that do not pose as Tailwind plugins: React components, checkout SDKs, "common" libraries, an MCP server, CLIs, a Babel config and copies of other libraries. They are listed separately below. One plugin, tailwindcss-effector, uses a variant that reads TRON and Ethereum transaction data instead. See all npm packages with remote code execution.

Fake job interviews and a private package

28 of the plugins come from one template, and their OpenSSF advisories describe a recruiting lure in the style of the "Contagious Interview" campaign. A fake company sends a take-home assessment repository. The advisories name two: "Nodveta", advertised on Torre.ai, and "BlockRoute Labs", which recruited through Djinni.

  1. The repository commits web/.npmrc with an npm auth token, and its tailwind.config.js loads one of these plugins.
  2. The plugin is a working Animate.css integration with no install script and no network code. It requires a private scoped package, @jasperquinn/postcss-motion-helpers or @aaron205whitmore/postcss-animate-utils.
  3. npm can fetch that private package only with the committed token, so npm audit and public scanners never see it.
  4. When the candidate starts the dev server, Tailwind loads the plugin and the private package runs. Public analysis of an earlier package in the chain reports that it contacts the operator's server and runs the code it receives.

The template was published from 16 npm accounts, most of which published two of the plugins. LPM's review of the public tarballs finds nothing, by design, so these packages are marked advisory below. LPM Firewall blocks them on the advisories. The private helper @jasperquinn/postcss-motion-helpers later appeared on the public registry as an empty placeholder and has its own advisory.

Code from a fixed server

  • 10getPlugin() loadersfetch JSON from a fixed server and run its credits field
  • 4fetch and run on buildobfuscated PostCSS plugins for animate.css
  • 4download and requiresave a script to the working directory and require it

The largest family exports a getPlugin() function. It fetches JSON from a fixed address such as 31.97.137.157:45000/icons/109 and passes the credits field to new Function along with require, process and Buffer. Several of these packages are named for Fastify or Polygon, but their READMEs present them as a "Tailwindcss Forms Bundler".

The rest use paste services such as jsonkeeper and npoint, a Google Apps Script, a URL read from node_modules/.bin/manifest.json, or a URL hidden in the pixels of a PNG. The Gutenberg block packages run a script with Deno from a bare IP address, and @tailwind-ts/eslint-plugin downloads, installs and runs a bundle from its postinstall script.

Payloads hidden in the package

Some packages carry the payload with them and rebuild it at run time:

  • tailwind-typography-plus rebuilds JavaScript from the "ratio" numbers in data/font-metrics.json.
  • gpu-accelerator XOR-decodes assets/driver-shim.bin, and postcss-processor-utils decodes data/design-tokens.json, writes it to the temp directory and requires it.
  • @tailwindxss/postcss, a lookalike of @tailwindcss/postcss, reads SVG files in the project's asset folders, base64-decodes their HTML comments and runs the result, so the code comes from the project rather than the package. LPM found it with no advisory.
  • postcss-minify-selector and postcss-selector-minify register themselves as cssnano's postcss-minify-selectors and load the payload from a second package, postcss-minify-selector-parser or layerd-unit-codec-parser.
  • noelc-css-blender, postcss-semantic-themes and birch-css-utils ship hundreds of kilobytes of obfuscated modules named like build caches. birch-css-utils asks to be installed as tailwindcss@npm:birch-css-utils, which replaces Tailwind itself.

For 23 more packages, the advisory gives no public description of the payload. LPM Firewall blocks them on the advisory.

Indicators of compromise

Blockchain loader

4

The RPC services the loader queries are legitimate. Block the paths, and treat requests to them from a build as a signal.

  • 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a (Ethereum sender address)
  • http://<ip>:443/0x/cls
  • http://<ip>:443/0x/ls
  • x-payload-b64 response header

Payload servers

10
  • https://31.97.137.157:45000/icons/<n>
  • http://46.183.25.232:45000/icons/106
  • https://bet.slotgambit.com/icons/111
  • https://svganchordev.net/icons/105
  • http://23.27.245.100/index.js
  • http://23.27.245.100:3000/index.js
  • http://tailwindlan.online
  • http://194.11.226.41:4000
  • http://172.94.9.157/v028f8cde892b0b74c8.js
  • https://slimopump.vercel.app/config/clob-math.json

Abused services

3

Block the paths, not the services.

  • https://www.jsonkeeper.com/b/NFTTN
  • https://api.npoint.io/24c25d5f5fcbb0992a4f
  • https://script.googleapis.com/v1/scripts/AKfycbyjrAVcSQB2V6XNzgYK8J0gGyx0c_ZpC0y4rFzq1IGUPu7zx7guLszZDVlcsfDzXVz3QA

Job interview lures

3
  • @jasperquinn/postcss-motion-helpers (private dependency)
  • @aaron205whitmore/postcss-animate-utils (private dependency)
  • web/.npmrc with an npm auth token, committed to the assessment repository

Files and code markers

7
  • global.r = require; global.m = module
  • build-cache.mjs, cache-warm.mjs, cache-sweep.mjs
  • data/font-metrics.json
  • assets/driver-shim.bin
  • data/design-tokens.json
  • node_modules/.bin/manifest.json
  • inout.js in the working directory

What to do

  • Search package.json files and lockfiles for the names below. The real plugins are tailwindcss, @tailwindcss/forms, @tailwindcss/typography, @tailwindcss/postcss and tailwindcss-animate.
  • Read tailwind.config.js and postcss.config.js as code. Every plugin they list runs in Node during each build.
  • Open take-home assignments in a disposable VM or container with no credentials. A repository that commits an .npmrc with a token is a red flag.
  • --ignore-scripts does not help here. Keep long-lived secrets out of build environments, and restrict outbound traffic from builds where you can.
  • If one of these packages ran, treat the machine as compromised. Rotate every credential it held, including cloud, npm, GitHub and wallet keys, and look for detached node processes.
  • Block the hosts and paths above at network egress.

Affected packages

148 packages, grouped by how the payload arrives, and 27 other packages with the same blockchain loader. Each name links to its LPM Firewall report and is marked LPM confirmed or advisory.

Server address on Ethereum45 packages
Fake job interviews29 packages
Fixed server or paste service31 packages
Payload in the package20 packages
Advisory only23 packages
Same blockchain loader, other disguises27 packages

How we identified the packages

The list covers every npm package LPM Firewall blocks that presents itself as a Tailwind CSS or PostCSS plugin, utility or lookalike, by name, description or README, as of Nov 3, 2026. It adds the other blocked packages that carry the same blockchain loader. Compromised legitimate packages and dependency confusion probes are left out. The groups come from LPM's reviews and the advisory text for each package.

The packages share a disguise and, within each group, a technique, but not necessarily one publisher. The transaction count comes from the public Ethereum record for the sender address.

Read how LPM Firewall reports are produced, or browse more research.