On September 18, 2026, French security company CrowdSec disclosed that an attacker copied approximately 170 of its private GitHub repositories on May 22, 2026, using the GitHub account of an employee who had just left the organization. The root cause chain is a textbook modern supply chain compromise: the former employee's laptop was compromised during the May 2026 TanStack npm supply chain attack, in which malicious versions of TanStack's widely-used npm packages harvested credentials from developer machines. CrowdSec had not yet revoked the departing employee's GitHub access — and the stolen credentials from the compromised laptop gave the attacker a live, authenticated path directly into CrowdSec's private source code.
Three failures compounded into one breach: (1) a poisoned upstream npm package executed on a developer workstation, (2) credentials stored on that workstation were exfiltrated, and (3) an offboarding gap left those credentials valid after the employee departed. If a security company with CrowdSec's maturity can be hit this way, every organization with developers pulling from npm and hosting private code on GitHub should treat this as a direct warning. Source code theft is not just an IP loss — private repos routinely contain hardcoded secrets, internal infrastructure details, and vulnerability context that fuel follow-on attacks.
Technical Analysis
Attack Chain
The intrusion followed a supply chain → endpoint → identity → data exfiltration pattern:
- Initial access — malicious npm package execution. The attacker published malicious versions of TanStack npm packages (TanStack maintains popular JavaScript libraries including React Query, TanStack Query, Router, and Table — packages with millions of weekly downloads). Compromised versions executed malicious code during installation or at runtime, consistent with the classic
postinstallscript / install-hook abuse pattern used in npm supply chain attacks. - Credential theft on the developer endpoint. The malicious payload stole credentials from the victim's laptop. On developer machines this typically means:
~/.npmrctokens,~/.git-credentialsand git credential helper stores, SSH keys in~/.ssh/, browser session cookies and tokens (including active GitHub sessions), environment variables, and cloud CLI credentials (~/.aws/,~/.azure/,~/.config/gcloud/). - Orphaned identity abuse. The victim had left CrowdSec, but his GitHub access remained active. Stolen-but-valid credentials are indistinguishable from legitimate use unless defenders monitor for behavioral anomalies.
- Bulk private repository exfiltration. Using the still-valid account, the attacker cloned/copied roughly 170 private repositories on May 22 — a volume of git clone activity that should be anomalous for any single user, especially a departed one.
Affected Scope
- Directly affected: CrowdSec — approximately 170 private GitHub repositories copied.
- At-risk population: Any organization whose developers installed the malicious TanStack package versions in May 2026, and any organization with offboarding gaps for SaaS/code-platform access (GitHub, GitLab, Bitbucket, Azure DevOps).
- Downstream risk: Exfiltrated source code may contain embedded secrets (API keys, tokens, connection strings), enabling secondary compromise. Treat any secrets that existed in cloned repositories as burned.
No CVE identifier has been published for this campaign in the source reporting; the threat is a malicious-package/credential-theft operation rather than a patched software flaw. There is no CVSS score to track — the remediation is procedural and detective, not a version bump (though removing malicious package versions is required).
Why This Works — Defender's Perspective
- npm install hooks run arbitrary code with the developer's privileges. A poisoned dependency inherits full access to everything the developer can read — including credential stores and active session tokens.
- GitHub (and similar platforms) trust the credential, not the context. A valid PAT, SSH key, or session token from an unusual ASN, geography, or at unusual volume will still succeed by default.
- Offboarding is frequently IdP-centric. Disabling an account in Okta/Entra ID does not automatically revoke GitHub org membership, personal access tokens, SSH keys, or active sessions if GitHub is not fully gated behind SSO enforcement. CrowdSec's disclosure is the canonical case study.
Detection & Response
This is a technical threat. The detections below target three observable layers: (A) malicious npm install behavior on endpoints, (B) credential file access by Node/npm processes, and (C) anomalous GitHub clone/exfiltration activity.
Sigma Rules
---
title: Suspicious npm Install Lifecycle Script Execution
id: 3f8a1c92-7d44-4e5b-a9c1-2b6d8f0e4a71
status: experimental
description: Detects npm spawning shell interpreters or system utilities during package install lifecycle hooks, a hallmark of malicious package postinstall/preinstall scripts as seen in the TanStack npm supply chain compromise.
references:
- https://thehackernews.com/2026/09/crowdsec-says-tanstack-npm-attack-led.html
- https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.initial_access
- attack.t1195.002
- attack.execution
- attack.t1059
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\node.exe'
- '\npm.cmd'
- '\npm.exe'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\curl.exe'
- '\certutil.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\bitsadmin.exe'
condition: selection_parent and selection_child
falsepositives:
- Legitimate build tooling (node-gyp, native module compilation) spawning cmd.exe during npm install
level: high
---
title: Node Process Accessing Developer Credential Stores
id: 8c2e5b14-1a93-4d6f-b7e2-9c4a6d1f8e35
status: experimental
description: Detects node/npm processes reading credential stores such as .npmrc, .git-credentials, SSH private keys, or cloud CLI credential files — consistent with credential harvesting payloads in malicious npm packages.
references:
- https://thehackernews.com/2026/09/crowdsec-says-tanstack-npm-attack-led.html
- https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.credential_access
- attack.t1552.001
- attack.t1552.004
logsource:
category: file_event
product: windows
detection:
selection_image:
Image|endswith:
- '\node.exe'
- '\npm.cmd'
- '\npm.exe'
selection_target:
TargetFilename|contains:
- '\.npmrc'
- '\.git-credentials'
- '\.ssh\id_'
- '\.aws\credentials'
- '\.azure\'
- '\gcloud\credentials'
- '\AppData\Local\Google\Chrome\User Data\Default\Cookies'
- '\AppData\Local\Google\Chrome\User Data\Default\Login Data'
condition: selection_image and selection_target
falsepositives:
- npm CLI legitimately reading .npmrc for registry authentication — tune by excluding read-only npm CLI operations where possible
level: high
---
title: Git Clone Via Stolen Credentials From Unusual Shell Context
id: 5d1f9a36-2e84-4c7a-b3d5-6a9c1e4f7b28
status: experimental
description: Detects git clone or git archive operations launched from script interpreters or unexpected parent processes, which may indicate automated bulk repository exfiltration using stolen credentials rather than interactive developer activity.
references:
- https://thehackernews.com/2026/09/crowdsec-says-tanstack-npm-attack-led.html
- https://attack.mitre.org/techniques/T1567/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.exfiltration
- attack.t1567
- attack.collection
- attack.t1213
logsource:
category: process_creation
product: windows
detection:
selection_git:
Image|endswith: '\git.exe'
CommandLine|contains:
- 'clone'
- 'archive'
- 'bundle create'
selection_parent:
ParentImage|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\python.exe'
- '\node.exe'
condition: selection_git and selection_parent
falsepositives:
- CI/CD build agents and automation scripts legitimately cloning repositories — scope to workstations and exclude known build hosts
level: medium
KQL — Microsoft Sentinel / Defender
The highest-fidelity signal in this incident lives in GitHub audit logs, not the endpoint. Ingest the GitHub Audit Log streaming feed into Sentinel, then hunt for bulk clone volume and impossible-to-miss identity anomalies. Endpoint hunting covers the credential theft stage.
// Hunt 1: Bulk repository clone/download activity per user (GitHub audit log ingested as custom table or via API)
// Adjust table name to your GitHub audit log ingestion (e.g., GitHubAuditLog_CL)
GitHubAuditLog_CL
| where TimeGenerated > ago(30d)
| where action_s in ("repo.clone", "repo.download_zip", "repo.archive", "git.clone")
| summarize RepoCount = dcount(repo_s), Clones = count(), Repos = make_set(repo_s, 50) by actor_s, bin(TimeGenerated, 1h)
| where RepoCount > 10 // threshold: tune to your org's baseline; 170 repos in a day is extreme
| sort by RepoCount desc
;
// Hunt 2: GitHub activity from accounts of deactivated/offboarded IdP users
let Offboarded =
IdentityInfo
| where TimeGenerated > ago(14d)
| where AccountEnabled == false or tostring( Tags ) has "offboarded"
| summarize by AccountUPN;
GitHubAuditLog_CL
| where TimeGenerated > ago(30d)
| where actor_s in (Offboarded)
| project TimeGenerated, actor_s, action_s, repo_s, remote_address_s, user_agent_s
| sort by TimeGenerated desc
;
// Hunt 3: Node/npm child processes accessing credential stores (MDE endpoint telemetry)
DeviceFileEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("node.exe", "npm.cmd", "npm.exe")
| where FileName has_any (".npmrc", ".git-credentials", "id_rsa", "id_ed25519", "credentials")
or FolderPath has_any ("\\.ssh\\", "\\.aws\\", "\\.azure\\")
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, FileName, FolderPath, AccountName
;
// Hunt 4: npm install spawning shells or download utilities (postinstall abuse)
DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("node.exe", "npm.cmd", "npm.exe")
| where FileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "curl.exe", "certutil.exe", "wscript.exe", "bitsadmin.exe")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
Velociraptor VQL
Use this artifact across developer workstations to find evidence of credential store access by Node processes and staged credential dumps.
-- Hunt: credential store artifacts accessed around suspected npm compromise windows
SELECT FullPath, Size, Mtime, Atime,
parse_string_with_regex(string=FullPath,
regex='(?i)(\.npmrc|\.git-credentials|id_rsa|id_ed25519|credentials)$') AS CredentialFile
FROM glob(globs=[
'C:/Users/*/.npmrc',
'C:/Users/*/.git-credentials',
'C:/Users/*/.ssh/id_*',
'C:/Users/*/.aws/credentials',
'C:/Users/*/AppData/Local/Google/Chrome/User Data/Default/Login Data'
])
WHERE Atime > parse(time='2026-05-01T00:00:00Z')
ORDER BY Atime DESC
-- Hunt: node/npm processes with suspicious command lines on endpoints
SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)node|npm'
AND CommandLine =~ '(?i)invoke|downloadstring|curl|wget|base64|-enc|postinstall'
Verification & Hardening Script
# Audit developer workstations for credential exposure and npm compromise indicators
# Run elevated; outputs findings to C:\IR-TanStack-Audit.txt
$report = "C:\IR-TanStack-Audit.txt"
"=== TanStack npm Supply Chain / Credential Exposure Audit - $(Get-Date) ===" | Out-File $report
# 1) Identify installed TanStack packages in common project locations (verify versions against vendor advisory)
"`n[1] TanStack package installations found:`n" | Out-File $report -Append
Get-ChildItem -Path "C:\Users" -Recurse -Filter "package-lock.json" -ErrorAction SilentlyContinue -Depth 6 |
Select-String -Pattern '"@tanstack/' | ForEach-Object {
"$($_.Path): $($_.Line.Trim())" | Out-File $report -Append
}
# 2) Check for exposed credential stores that malicious packages target
"`n[2] Credential stores present on this machine:`n" | Out-File $report -Append
$paths = @("$env:USERPROFILE\.npmrc", "$env:USERPROFILE\.git-credentials",
"$env:USERPROFILE\.ssh\id_rsa", "$env:USERPROFILE\.ssh\id_ed25519",
"$env:USERPROFILE\.aws\credentials")
foreach ($p in $paths) { if (Test-Path $p) { "EXPOSED: $p" | Out-File $report -Append } }
# 3) Check git credential helper configuration (prefer manager-core with OAuth over plaintext store)
"`n[3] Git credential helper configuration:`n" | Out-File $report -Append
git config --global credential.helper | Out-File $report -Append
# 4) Flag plaintext git credential storage
if ((git config --global credential.helper) -eq "store") {
"WARNING: plaintext credential.helper=store configured - rotate GitHub credentials immediately" |
Out-File $report -Append
}
#!/bin/bash
# Linux/macOS developer endpoint audit for npm credential-theft exposure
REPORT="/tmp/tanstack_audit_$(hostname)_$(date +%Y%m%d).txt"
echo "=== TanStack npm Credential Exposure Audit - $(date) ===" > "$REPORT"
# 1) Find malicious-version candidates in lockfiles (cross-reference with TanStack/GitHub advisories)
echo -e "\n[1] TanStack dependencies in lockfiles:" >> "$REPORT"
grep -rl '@tanstack/' /home --include=package-lock.json --include=yarn.lock --include=pnpm-lock.yaml 2>/dev/null | while read -r f; do
echo "FOUND: $f" >> "$REPORT"
grep -A1 '"@tanstack/' "$f" | grep '"version"' >> "$REPORT"
done
# 2) Check for postinstall hooks in installed node_modules (high-signal IoA)
echo -e "\n[2] Packages with install lifecycle scripts:" >> "$REPORT"
find /home -name package.json -path '*/node_modules/*' -exec grep -l '"postinstall"\|"preinstall"' {} \; 2>/dev/null | head -50 >> "$REPORT"
# 3) Flag exposed credential files
echo -e "\n[3] Credential stores present:" >> "$REPORT"
for f in ~/.npmrc ~/.git-credentials ~/.ssh/id_rsa ~/.ssh/id_ed25519 ~/.aws/credentials; do
[ -f "$f" ] && echo "EXPOSED: $f" >> "$REPORT"
done
# 4) Check git credential helper mode
echo -e "\n[4] Git credential helper: $(git config --global credential.helper)" >> "$REPORT"
[ "$(git config --global credential.helper)" = "store" ] && \
echo "WARNING: plaintext credential store in use - rotate GitHub tokens" >> "$REPORT"
echo "Report written to $REPORT"
Remediation
Immediate Actions (24–48 hours)
- Determine exposure to malicious TanStack versions. Inventory every lockfile (
package-lock.json,yarn.lock,pnpm-lock.yaml) across repos and build artifacts for@tanstack/*packages installed during the May 2026 compromise window. Cross-reference exact versions against the TanStack/GitHub security advisories for the incident and purge malicious versions from registries, caches (npm cache clean --force), and private mirrors (Verdaccio, Artifactory, Nexus). - Rotate every credential that touched a potentially compromised developer endpoint. This includes GitHub personal access tokens, SSH keys, npm tokens, cloud CLI credentials, and browser-stored sessions. Assume any credential file readable by the developer's user context is burned — that is exactly what the CrowdSec case demonstrates.
- Audit GitHub for orphaned access. Enumerate organization members and outside collaborators; cross-reference against HR/IdP offboarding records. CrowdSec's breach happened because a departed employee's access remained valid. Revoke dormant PATs and SSH keys (
Settings → Third-party access / Authorized OAuth Apps, plus org-level token review). - Review GitHub audit logs for bulk clone activity. Pull
repo.clone,repo.download_zip, andgit.cloneevents for the last 90 days and baseline per-user repository volume. A single account touching 170 private repos in a short window — as in this incident — must page someone.
Structural Fixes (30 days)
- Enforce SSO and SCIM for GitHub. Route all org access through your IdP with SAML enforcement and SCIM provisioning so IdP deactivation automatically removes GitHub access. This eliminates the orphaned-account failure mode entirely. Enable IP allow lists or Conditional Access policies for git operations where feasible.
- Neutralize npm install hooks in production builds. Use
npm ci --ignore-scriptsin CI/CD, enable--auditgating, and consider Socket, npq, or pnpm'sneverBuiltDependenciescontrols. Pin dependencies by lockfile and verify package integrity hashes. - Shorten credential lifetimes. Replace long-lived PATs with fine-grained tokens (90-day max expiry), GitHub Apps with installation tokens for automation, and require 2FA/hardware keys org-wide.
- Scan all private repos for secrets now. If an exfiltration event occurred, run gitleaks/trufflehog across all private repositories and rotate every discovered secret — attackers mine cloned repos for keys as a first move.
- Alert on departed-user activity. Wire IdP deactivation events into your SIEM and alert on any downstream SaaS/code-platform activity from accounts disabled in the IdP. Zero is the only acceptable count.
Validation
After remediation, run the audit scripts above against a sample of developer endpoints, verify GitHub org membership matches active IdP accounts 1:1, and confirm clone-volume detection rules fire in a tabletop test. CrowdSec's transparency here is a service to the industry — the organizations that benefit are the ones that treat this disclosure as a checklist, not a news story.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.