Executive summary

Wiz reported on 19 May 2026 a supply-chain campaign affecting npm packages, GitHub Actions and the Nx Console extension. Nx’s advisory, updated on 21 May 2026, confirms that extension version 18.95.0 was malicious and directs affected users to update, stop malicious processes, remove persistence and rotate reachable credentials. We assess it is likely (B2) that organisations need one coordinated investigation across these distribution channels, because checking only an application’s dependency list leaves developer extensions and build automation outside the scope.

Contextual background

The Nx maintainer advisory records malicious extension publication on 18 May 2026. Socket’s report on 19 May 2026 describes malicious package publication across the @antv ecosystem and related packages outside that namespace. Wiz places the extension, package and GitHub Actions findings within a broader campaign involving credential theft and persistence.

Wiz’s explicit attribution assessment is moderate confidence for TeamPCP, based on infrastructure, functionality and operational overlaps. Retain that qualifier even though the publisher’s headline uses the actor name directly. It is an assessment by Wiz, not a confirmed identity for every account or repository involved in the distribution chain.

The common operational problem is trusted tooling executing with developer or build-system access. Package installation, editor extension activation and workflow execution are different triggers. An inventory covering only production application dependencies may therefore miss the workstation or release identity through which consequential credentials became available.

Affected components and exposure windows

Nx identifies 18.95.0 as the affected Nx Console version and 18.100.0 as the corrected version. Its revised timeline says the malicious release was available in Visual Studio Marketplace from 12:30 UTC to 12:48 UTC on 18 May 2026, and in OpenVSX from 12:33 UTC to 13:09 UTC on 18 May 2026. Both distribution channels belong in the investigation.

The maintainer distinguishes platform download figures from its own activation telemetry and reports a substantial disagreement between them. These measure different events and should not be turned into a single precise victim count. Our recommendation is to establish local extension versions, installation history and execution evidence rather than use a marketplace total to infer whether a specific organisation was affected.

Wiz lists actions-cool/issues-helper and actions-cool/maintain-one-comment as affected GitHub Actions, with all tags affected for a limited period. That wording requires a historical workflow check. The tag visible today does not establish which content a past job retrieved; retain the workflow run, resolved revision and execution interval wherever available.

For npm, use the version-specific lists in Socket and Wiz. Socket identifies packages across visualisation and related ecosystems, including names outside @antv. Do not reduce the hunt to a namespace prefix or treat every historical version of every named package as malicious.

Technical analysis

Socket describes an installation-time payload in compromised npm packages, using an obfuscated JavaScript file and Bun execution. Its analysis identifies credential collection, direct HTTPS exfiltration and a GitHub-based fallback. It also describes propagation logic that can use available publishing credentials to modify and release additional packages. The existence of that capability does not prove that every affected developer account published a malicious successor.

Wiz describes payload delivery through GitHub-hosted material, including orphaned commits, followed by credential collection and persistence. Targets include GitHub tokens, SSH keys, cloud credentials and other developer authentication material. A trusted hosting domain therefore does not establish that every retrieved object is safe; the relevant evidence includes the object, identity, execution chain and authorised purpose.

The reported persistent Python component is stored at ~/.local/share/kitty/cat.py. Wiz describes polling for signed instructions through GitHub and states that it had not observed the command-and-control infrastructure active at the time of its report. That observation concerns the described command channel. It should not be broadened into a claim that the initial payload did not execute or that credentials were not exfiltrated.

Nx’s advisory independently describes credential harvesting from disk and memory and exfiltration through HTTPS, GitHub and DNS. It includes conditional targets, such as 1Password material where an active command-line session was available. Our recommendation is to preserve these prerequisites when assessing an individual machine rather than assume every listed store was accessible.

Root cause and publishing authority

In its 21 May 2026 update, Nx says a developer was affected by a TanStack supply-chain compromise that exposed GitHub credentials through the GitHub command-line tool. The stolen access enabled the attacker to run repository workflows as a contributor. This is the maintainer’s account of the Nx incident; it should not be assumed to explain the entry route for every other package in the wider campaign.

Nx says it changed extension publishing from a process available to one member without manual approval to one requiring approval from two administrators. Our recommended lesson is to examine the authority of a stolen contributor identity and the approval boundary around releases. Review who can change the workflow as well as who can initiate it, and test those permissions against the intended release process.

Selected indicators and hunting leads

These selected artefacts appear in the Nx advisory and Wiz report. Their role and surrounding evidence matter more than a name-only match.

Artefact Reported context Recommended investigation
Nx Console 18.95.0 Malicious extension release Establish installation and activation history
~/.local/share/kitty/cat.py Persistent Python component Preserve the file and identify its launching process
~/Library/LaunchAgents/com.user.kitty-monitor.plist macOS persistence Inspect the registered job before removal
/var/tmp/.gh_update_state and /tmp/kitty-* Maintainer-listed compromise indicators Correlate with extension and process history
niagA oG eW ereH :duluH-iahS Repository description used in the campaign Review repository creation and the responsible account
firedalazer Reported command-message marker Correlate with suspicious polling and the local component

Our recommendation is to treat a marker search as a lead rather than a complete population count. Repositories can contain research, copied examples or unrelated references. Confirm the creation event, content and responsible identity before counting a result as an exfiltration repository or an affected organisation.

Detection and hunting guidance

Our recommended hunt builds an execution inventory across three channels: npm installs, editor extension activity and GitHub Actions runs. For each candidate, record the host or runner, executing identity, time and evidence level. A manifest reference, downloaded archive and observed execution are different findings and may justify different response scope.

Then examine the identity path. Determine which credentials were present or obtainable in that environment, inspect relevant provider audit logs and look for unauthorised repository creation, workflow changes or package publications. Compare the activity with legitimate maintenance and release jobs. A credential’s presence establishes potential exposure; verified use provides stronger evidence about downstream impact.

Review current and historical state together. A corrected extension version can coexist with processes started by an earlier malicious release. A clean dependency lockfile may describe a later rollback rather than the version installed during the incident. Preserve build logs, caches and endpoint events where available instead of treating the current inventory as the complete history.

Incident response guidance

Nx explicitly directs affected or potentially affected users to complete all its remediation steps: update to 18.100.0 or later, stop the malicious processes, remove persistence artefacts, rotate reachable credentials and audit account access. An extension upgrade is one part of that procedure, not a substitute for the rest.

The maintainer highlights a macOS ordering requirement: unload the malicious LaunchAgent before deleting its file, because deleting the file alone can leave it active in launchd. Use the maintainer’s procedure and the organisation’s incident process, preserving necessary evidence before removal. Verify the running state after the change rather than relying only on the file’s absence.

Our recommended credential recovery begins from a trusted administrative environment after the active theft path has been contained. Map tokens, keys and sessions to their services and permissions, then use each provider’s supported revocation procedure. Review actions already performed with those identities, particularly downstream releases and workflow changes that remain available to other users.

For build infrastructure, reconstruct affected releases from retained job and artefact records. Determine whether the compromised environment had publishing authority and whether that authority was exercised. A successful rebuild using clean inputs helps restore the release path, but it does not by itself establish that earlier published artefacts were trustworthy.

Impact and mitigation priorities

The reported credential targets make workstations and automation environments consequential even when the application being developed is not itself sensitive. Our recommendation is to assess the permissions present at execution time, including release credentials and interactive developer sessions, rather than use the application’s business classification as the sole measure of impact.

Reduce unnecessary secrets in dependency and extension evaluation environments. Review publishing approvals, limit the authority available to ordinary build jobs and retain resolved dependency and workflow revisions. Test how quickly a malicious version can be rejected in internal distribution paths and how the organisation identifies machines that already received it.

Our recommended closure record separates local recovery from downstream integrity. It should show that persistent execution has stopped, relevant credentials have been addressed and unauthorised publications or repository changes have been investigated. Assign unresolved checks to the service owner who can verify them rather than ending the investigation when the initial extension is removed.

Our recommended release review also checks reusable templates and developer setup instructions. A clean central workflow is insufficient if another maintained repository still references the affected Action or an internal extension cache retains the malicious release. Use the inventory to identify each distribution path, assign its owner and verify the replacement through a normal installation or build using trusted inputs.

Keep incident scope tied to evidence. An affected package version in an old lockfile deserves investigation, but it does not establish execution on every developer machine. Conversely, a missing cache file after routine cleanup does not resolve a retained log showing the malicious installation ran. Record the strongest available evidence for each system.

Threat intelligence context and outlook

Our ATT&CK mappings are T1195.002: Compromise Software Supply Chain and T1552.001: Credentials In Files, reflecting malicious distribution through development tooling and collection of stored credentials. They describe reported behaviour rather than an additional attribution finding.

Follow maintainers’ revised scope while keeping the evidence behind earlier decisions. The immediate objective is a reliable account of which component ran, under which identity, with what access and what happened afterwards. That account remains useful even when a package is removed from a registry or a campaign changes its branding.

Further reading