A months-old thread in the Shai-Hulud npm worm campaign has surfaced a new victim: CrowdSec, the French open-source cybersecurity firm best known for its crowdsourced IP-reputation engine. According to Dark Reading, threat actors used a GitHub OAuth token — stolen from a former employee's machine via May's TanStack npm supply chain attack — to clone roughly 170 of CrowdSec's private GitHub repositories. The theft happened in May 2026 but stayed invisible until the stolen archive turned up on a cybercrime forum on September 16, nearly four months later.
The Attack Chain: From a Poisoned npm Package to 170 Stolen Repos
The chain starts with the TanStack npm compromise, tracked as CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx. On May 11, 2026, the group tracked as TeamPCP (also known as UNC6780) hijacked TanStack's GitHub Actions release pipeline using a "Pwn Request" pattern combined with cache poisoning and in-memory OIDC token extraction from the runner process. That let them publish 84 malicious versions across 42 @tanstack/* packages using TanStack's own legitimate, trusted publishing identity — reportedly the first documented case of a malicious npm package carrying valid SLSA provenance. Anyone who installed one of those versions ran a credential-harvesting payload from the Shai-Hulud malware family, which sweeps up GitHub and npm tokens, SSH keys, and AWS/GCP/Azure credentials, then tries to inject itself into any other package the victim can publish — worm-like propagation that helped the campaign spread to 170-plus packages across npm and PyPI by the end of that day, hitting projects tied to Mistral AI, UiPath, OpenSearch, and Guardrails AI.
A former CrowdSec engineer's personal machine was among the infected devices. He had already left the company, but his GitHub organization access had been deliberately left active so he could finish outstanding work — a common, if risky, offboarding accommodation. Between 05:52 and 06:01 UTC on May 22, 2026, whoever now held his stolen gho_ OAuth token used it to clone approximately 170 private repositories in about nine minutes, from an IP address geolocated to Toronto, Canada, with a system clock set to UTC-4. CrowdSec removed the account from its GitHub organization on May 25 — three days later, and well after the data was already gone.
The breach then sat undiscovered for nearly four months. CrowdSec's own audit logs didn't help much at first: the token had already expired or been revoked by the time anyone went looking, so it didn't stand out. GitHub's support team ultimately had to help CrowdSec reconstruct the token's lifecycle and pull the git clone activity records that confirmed what happened. The stolen code only became visible to CrowdSec when it appeared on a cybercrime forum on September 16, reportedly posted by an account tied to a BreachForums founder and a user going by "diencracked."
Who CrowdSec Is — and Why This Breach Stings
CrowdSec builds an open-source, MIT-licensed security engine that parses log sources and HTTP traffic to detect malicious behavior, then shares that intelligence across its entire user base — the idea being that an attack detected on one participant's server helps protect thousands of others in near real time. Its commercial business sits on top of that open core: curated premium blocklists, high-volume threat-intel API access, and a managed console, sold to SOC teams, DevSecOps shops, hosting providers, and MSSPs.
That makes this incident more than a little uncomfortable. A company whose entire pitch is "detect it once, protect everyone" got compromised by one of the most ordinary failure modes in security: a departing employee's access that was never fully cut off. CrowdSec says the intrusion did not touch its build pipelines, production infrastructure, or databases, and that the damage was limited to source code and a handful of internal datasets — including 83 user email addresses, the names, emails, and investment context of 51 potential investors, and one scoped AWS credential limited to publishing on a single SNS topic. That AWS credential saw some probing on August 17 (GetCallerIdentity and ListTopics calls), but CrowdSec reports no further activity — a small proof that scoping a credential tightly can cap the damage even after it leaks.
CrowdSec is also not alone in this wave. The same TanStack compromise has already been linked to disclosures from Grafana and OpenAI, among others, underscoring how one poisoned dependency can ripple outward into unrelated companies' GitHub estates for months.
Part of a Bigger Pattern: The Shai-Hulud Campaign
"Shai-Hulud" isn't a one-off incident — it's become the name for an ongoing, evolving npm supply-chain worm campaign. The name comes from Frank Herbert's Dune, after the giant sandworms of Arrakis, a reference researchers picked up from an artifact filename in the malware's exfiltration code. The original worm was first documented in September 2025, when a self-replicating package (rxnt-authentication version 0.0.3) became "patient zero" for what was, at the time, the first true self-propagating worm seen in the npm ecosystem.
Since then, successive waves — often labeled "Mini Shai-Hulud" or tracked under related names like ChainDrop — have kept reusing the same Dune-themed conventions in their internal strings and exfiltration repo names (terms like sietch, mentat, and sandworm show up repeatedly) while targeting new sets of packages. TeamPCP, the group behind the May TanStack compromise, has a track record in this campaign: it's also been tied to the compromise of Aqua Security's Trivy scanner in March 2026, the Bitwarden CLI npm package in April 2026, and a batch of SAP npm packages in late April 2026, before moving on to TanStack in May. The CrowdSec disclosure is best understood as a delayed, downstream consequence of that May attack rather than a new intrusion in its own right — a reminder that a single poisoned dependency can keep producing victims for months after the packages themselves are pulled.
Why This Matters
The technical root cause that let a four-month-old npm worm infection turn into a stolen source-code archive wasn't a zero-day or a novel exploit — it was offboarding hygiene. CrowdSec kept the departing engineer's GitHub access alive to let him wrap up work, and that access included an OAuth token that kept working long after his employment, and apparently his other systems access, had ended. GitHub OAuth tokens don't expire on a fixed schedule by default; they remain valid until someone explicitly revokes the authorization, independent of whether the person behind them still has a badge, a laptop, or a reason to have that access at all.
That gap is exactly what turned a credential-stealing infection on one former employee's personal machine into a 170-repository breach of a cybersecurity vendor. It's a pattern worth internalizing: identity offboarding needs to cover every credential surface, not just the ones that show up in the primary directory or SSO provider.
Protective Measures
- Tie token revocation to offboarding, automatically. Identity governance workflows should revoke OAuth app authorizations and GitHub org access the moment an employee's status changes — not on a manual follow-up ticket.
- Prefer short-lived, scoped credentials. Fine-grained personal access tokens with expiration dates, and GitHub Apps issuing short-lived installation tokens instead of long-lived OAuth grants, shrink the window an attacker can exploit if a token does leak.
- Scan for anomalous npm supply-chain behavior before install. Software composition analysis tooling that flags suspicious postinstall scripts, unexpected lockfile changes, or known Shai-Hulud indicators can catch a poisoned package before it ever runs on a developer machine.
- Alert on clone velocity and volume. Cloning 170 repositories in nine minutes is not normal developer behavior; GitHub audit-log monitoring tuned to flag that kind of burst activity could have surfaced this in May instead of September.
- Scope credentials to the smallest possible blast radius. CrowdSec's exposed AWS key could only publish to one SNS topic — a small design choice that appears to have kept a bad situation from getting worse.