In July 2025, the npm publishing account behind eslint-config-prettier — one of the most widely used configuration packages in the JavaScript ecosystem — was hijacked and used to push malicious releases to millions of downstream projects. The attacker didn't exploit a code vulnerability; they phished the maintainer's npm credentials, then used the stolen access to publish tampered versions of not just eslint-config-prettier but four other packages controlled by the same account: eslint-plugin-prettier, synckit, @pkgr/core, and napi-postinstall. Because these packages sit deep in the dependency tree of linting and build tooling used by an enormous share of Node.js projects, the blast radius was immediate even though the compromise window was short. Security researchers at Socket flagged the malicious publishes within hours, and npm pulled the versions before most CI pipelines had a chance to install them — but the incident is a clean case study in how a single phished maintainer account cascades across an entire dependency graph.
What happened to the eslint-config-prettier npm account?
A threat actor gained control of the npm account that publishes eslint-config-prettier and used it to release versions containing injected postinstall code. eslint-config-prettier is a shared-config package with tens of millions of weekly npm downloads, used to disable ESLint rules that conflict with Prettier formatting — meaning it's pulled into the devDependencies of most modern JavaScript and TypeScript projects, from small open-source libraries to enterprise monorepos. Because the same npm account also controlled eslint-plugin-prettier, synckit, @pkgr/core, and napi-postinstall, the attacker published malicious versions of all five packages within a short window on the same day, rather than needing five separate compromises.
Which packages and versions were affected?
The compromise touched eslint-config-prettier, eslint-plugin-prettier, synckit, @pkgr/core, and napi-postinstall, all maintained under the same publishing account. Researchers identified the malicious releases as newly-published patch and minor versions layered on top of otherwise-legitimate release histories — the kind of update a dependency bot or npm install would pull down without a second look, since the package names, maintainers, and repository links all appeared normal. This is the pattern that makes account-takeover attacks harder to catch than typosquats: there's no misspelled package name to flag, because the attacker is publishing from the real, previously-trusted source.
How did the attacker get into a trusted maintainer's account?
The attacker used a phishing email that impersonated npm's support team to trick the maintainer into entering their credentials and one-time 2FA code on a lookalike login page. This is the same social-engineering pattern behind several other high-profile 2025 npm incidents — attackers don't need to find a zero-day in npm's auth system when a convincingly-spoofed "your account will be suspended" email works on a busy open-source maintainer who manages dozens of packages as an unpaid side project. Once the credentials and session token were captured, the attacker had full publish rights without needing to touch the maintainer's actual password manager or device.
What did the malicious code actually do?
The injected code ran as a postinstall script that checked for a Windows environment before attempting to execute a follow-on payload, meaning Linux and macOS installs — the majority of CI runners and developer machines in most orgs — were not the primary target. On matching systems, the script attempted to drop and launch an additional binary rather than immediately exfiltrating data, which is consistent with a loader stage rather than a complete payload. Because Socket's detection and npm's takedown happened within hours of publication, researchers did not observe widespread confirmed execution of the second-stage payload in the wild, but any project that ran npm install or npm ci against the compromised version range during that window pulled the code onto its machine regardless of whether the payload fully executed.
How exposed was the JavaScript ecosystem, really?
The exposure was large on paper because synckit and eslint-plugin-prettier are transitive dependencies of tooling far beyond Prettier itself — synckit alone is pulled in by multiple popular linting and build packages, meaning a project could have been exposed without eslint-config-prettier appearing anywhere in its own package.json. Combined weekly download counts across the five affected packages run well into the tens of millions, which is why security teams tracking the disclosure treated it as a high-urgency advisory rather than a routine dependency bump, even though the practical exploitation window was measured in hours, not days.
How was it contained, and what should teams check now?
npm removed the malicious versions and the maintainer regained control of the account within hours of Socket's disclosure, and GitHub Security Advisories were published for each affected package so that automated scanners could flag the exact bad version ranges. Teams should check their lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) for the specific compromised version ranges of eslint-config-prettier, eslint-plugin-prettier, synckit, @pkgr/core, and napi-postinstall installed around the disclosure date, rotate any CI or developer tokens that were active on machines that ran an install during that window, and audit CI logs for outbound connections tied to the postinstall step. Because the attack relied on a routine npm install, standard code review wouldn't have caught it — the malicious behavior lived in a dependency's install script, not in application code a human ever reads.
How Safeguard Helps
Safeguard is built for exactly this failure mode: dependencies you never directly review that get pulled in and executed automatically. Safeguard's SBOM generation and ingest continuously map every transitive package — including deep ones like synckit that never show up in a top-level package.json — so a compromised-package advisory can be matched against your actual install footprint in minutes instead of a manual grep across repos. Reachability analysis then tells you whether the vulnerable code path is actually loaded and executed in your build versus sitting unused, cutting through the noise of "affected package present, but does it matter here." Griffin AI, Safeguard's detection engine, is trained to flag anomalous postinstall scripts and newly-published versions from previously-quiet maintainer accounts — the exact signature this incident produced — and Safeguard's auto-fix PRs can pin or roll back affected dependency versions across every repository the moment an advisory lands, so remediation doesn't wait on each team to notice the CVE on their own.