On September 8, 2025, a phishing email spoofing npm's support team convinced a well-known maintainer to hand over account access — and turned an ordinary Monday into one of the most instructive react npm supply chain incident case studies in recent memory. The compromised packages, chalk and debug among them, aren't part of React or Next.js themselves. But they sit so deep in the dependency graph of webpack, Babel, ESLint, Jest, and the CLI tooling bundled inside create-react-app and Next.js that a large share of the JavaScript ecosystem's build pipelines pulled a poisoned version within hours of publication. It's a clean illustration of how a single stolen credential, three layers removed from any application code, can still land inside a production build.
This post walks through what actually happened, what was affected, how the incident scores against standard vulnerability frameworks (and where those frameworks fall short), and what teams running React or Next.js builds should do about it.
What Happened: A React NPM Supply Chain Incident Rooted in a Phishing Email
The attacker sent a targeted phishing email impersonating npm's support system, warning the maintainer known as "Qix" (Josh Junon) that two-factor authentication needed to be renewed. The linked page harvested npm credentials and a valid 2FA code, which the attacker used to publish new, malicious versions of packages the maintainer controlled — most notably chalk and debug, two of the most widely used utility libraries in the Node.js ecosystem, used for colored terminal output and diagnostic logging respectively.
The injected code was a browser-based "crypto clipper": obfuscated JavaScript that hooked window.ethereum, fetch, and XMLHttpRequest in a running application to silently rewrite cryptocurrency wallet addresses and transaction data before it reached the user's wallet extension. It did not target build servers directly in this first wave — its payoff depended on the tampered code shipping all the way through to a browser bundle, which made bundlers, minifiers, and CI pipelines the delivery mechanism even though they weren't the end target.
Because chalk and debug are transitive dependencies of an enormous number of build tools, this was functionally a malicious npm dependency incident that any project running npm install or next build during the exposure window could have picked up without a single direct dependency change in its own package.json.
Affected Versions and Components
The malicious publish affected a cluster of tightly related packages maintained by the same account, including:
chalk(malicious release5.6.1)debug(malicious release4.4.2)ansi-styles,supports-color,strip-ansi,ansi-regex,wrap-ansi,slice-ansicolor-convert,color-name,is-arrayish,simple-swizzleerror-ex,has-ansi,chalk-template
Each package received one poisoned point release. Combined, these libraries are pulled in — usually transitively, via webpack, Babel loaders, ESLint, Jest, and terminal-output helpers — by nearly every modern JavaScript toolchain, including the ones underneath Next.js and Create React App. For teams building on Next.js, the practical effect was a nextjs build pipeline compromise by proxy: next build, next dev, and CI jobs that resolved a fresh lockfile during the exposure window could install a tainted transitive dependency even though Next.js's own published packages were never touched. Projects with committed lockfiles that weren't regenerated during the window were not automatically exposed, which is exactly why lockfile hygiene turned out to matter more than most teams expected going into this.
CVSS, EPSS, and KEV Context
This incident is a good example of where traditional vulnerability scoring doesn't map cleanly onto supply chain compromises. There is no single CVE covering the event — GitHub instead issued separate GHSA advisories per affected package, each describing the specific malicious version and classifying it as malware rather than a conventional exploitable flaw. GHSA malware advisories of this kind are typically tagged Critical severity, but because the issue is injected code rather than a scored defect in otherwise-legitimate logic, there isn't a single authoritative CVSS vector that meaningfully describes it the way there would be for, say, a deserialization bug.
EPSS (the Exploit Prediction Scoring System) is built to estimate the probability that a known CVE will be exploited in the wild; it doesn't apply here because the packages themselves weren't vulnerable — they were actively weaponized at publish time, and the "vulnerability" existed only in each malicious version, not in the package's design. Likewise, CISA's Known Exploited Vulnerabilities (KEV) catalog tracks exploited vulnerabilities against specific CVE IDs and products, and supply chain malware injections in open-source registries generally fall outside its scope. That gap is worth naming explicitly: teams relying solely on CVE/CVSS/KEV feeds to prioritize risk had no scoring signal to catch this at all. The only real signals were registry-level anomaly detection and community reporting.
Timeline
- Morning, September 8, 2025 — Phishing email impersonating npm support is sent to maintainer "Qix," requesting a 2FA reset via a spoofed login page.
- Shortly after — Attacker uses harvested credentials and a valid one-time code to publish new versions of chalk, debug, and over a dozen related packages, each containing obfuscated crypto-clipper malware.
- Within hours — Independent researchers and the Aikido and Socket security teams flag anomalous published versions with no corresponding source commits or changelog entries, and the compromise becomes public.
- Same day — npm and GitHub pull the malicious versions from the registry; GHSA advisories are published per affected package; maintainers roll out clean patch releases.
- Following week — A separate, self-propagating campaign (widely referred to as "Shai-Hulud") compromises additional npm packages using stolen publish tokens, this time running secret-scanning tooling inside postinstall scripts on developer and CI machines and exfiltrating discovered credentials — a shift from browser-runtime targeting toward direct build-pipeline and CI credential theft.
Taken together, the two waves form a useful javascript supply chain attack case study in escalation: the first incident weaponized runtime code delivered through the build pipeline, and the second treated the build pipeline itself — its postinstall hooks, its CI secrets, its publish tokens — as the actual target.
Remediation Steps
Teams that build on React or Next.js should treat this as a checklist, not a one-time cleanup:
- Audit lockfiles. Search
package-lock.json,yarn.lock, andpnpm-lock.yamlfor the affected packages and pin to versions published after the incident (or confirmed pre-compromise versions). - Regenerate and re-review any build artifacts produced during the exposure window, particularly client bundles that touch wallet or payment integrations.
- Rotate credentials for any CI system that ran
npm installagainst an unpinned range during the exposure window, since a later campaign specifically targeted CI secrets. - Enforce npm provenance and 2FA for all publishing accounts on packages your org maintains, and require hardware-key-based 2FA rather than TOTP where npm supports it.
- Run installs with
--ignore-scriptsby default in CI, allowlisting only the packages that legitimately require postinstall behavior. - Adopt lockfile-integrity and dependency-drift monitoring so a package version bump with no matching source diff or GitHub release triggers an alert before it reaches a build.
- Generate and diff SBOMs across builds so a new transitive dependency — or a same-version package with a different hash — is visible immediately rather than discovered after the fact.
How Safeguard Helps
Incidents like this expose a structural blind spot: CVE databases, CVSS scores, and even KEV listings are built around known vulnerabilities in named software, not zero-day malware injected into a trusted package by a compromised maintainer account. Safeguard is built for exactly that gap.
Safeguard continuously generates and diffs SBOMs across every build, so when a transitive dependency like debug or chalk changes hash or version without a corresponding source commit or release note, it's flagged before that build reaches production — not after a researcher tweets about it. Publish-time anomaly detection watches upstream registries for exactly this pattern: a maintainer account suddenly shipping a version with no changelog, an unusual file added to the package tarball, or a postinstall script appearing where none existed before. For CI and build pipelines specifically, Safeguard enforces policies like blocking arbitrary postinstall script execution, pinning dependency ranges, and requiring provenance attestation, closing off both the "runtime payload" pattern seen in the chalk/debug compromise and the "steal CI secrets" pattern seen in the worm campaign that followed it. And because Safeguard treats supply chain risk as a continuous signal rather than a periodic scan, teams get alerted the moment a known-compromised package version enters a lockfile anywhere in their organization — turning what was, for most of the ecosystem, a multi-hour blind spot into a same-hour catch.
The npm ecosystem will keep producing incidents that don't fit neatly into a CVE record. Building a pipeline that can catch them anyway is the actual lesson of this one.