The security of the npm ecosystem reached a critical inflection point in September 2025. The Shai-Hulud worm, a self-replicating malware that automated the compromise and redistribution of malicious packages, marked the end of the “nuisance” era of npm attacks and the beginning of a high-consequence threat landscape.
Since that watershed moment, Unit 42 has tracked an aggressive acceleration in the frequency and technical depth of supply chain compromises. Attacks have evolved from a series of isolated typosquatting incidents into systematic campaigns by various threat actors to weaponize the trust that powers modern software development.
The Shai-Hulud incident proved that the npm registry could be used as a force multiplier for malware distribution. In the months following, we have observed three core shifts in adversary TTPs:
npm compromises have common themes. In the post-Shai-Hulud era, we believe it is helpful to consider the attack surface as a whole.
This article will combine:
A malicious npm package published as @bitwarden/cli version 2026.4.0 was identified as part of a broader supply-chain campaign attributed to TeamPCP. The package impersonates the legitimate Bitwarden CLI password manager. Upon installation, it executes a multi-stage payload that steals credentials from cloud providers, CI/CD systems and developer workstations. It then self-propagates by backdooring every npm package the victim can publish. It has been noted that inside public GitHub repositories that were published contained the string “Shai-Hulud: The Third Coming.”
Attackers deployed the same payload across multiple Checkmarx distribution channels, indicating a coordinated campaign to weaponize compromised developer tooling credentials to maximize the area of impact:
Palo Alto Networks customers are better protected from the threats described in this article through the following products and services:
The Unit 42 Incident Response team can also be engaged to help with a compromise or to provide a proactive assessment to lower your risk.
According to Checkmarx's official security update, this npm package is one component of a broader supply-chain campaign that simultaneously compromised multiple Checkmarx distribution channels:
Per Checkmarx's disclosure, all artifacts share the same C2 infrastructure (audit.checkmarx[.]cx), the same obfuscation techniques and the same credential harvesting and propagation logic. The VS Code extension variant delivered its payload (mcpAddon.js) from a backdated orphan commit in Checkmarx's own GitHub repository, making the download URL appear trustworthy.
TeamPCP (@pcpcats) publicly took credit for the compromise. Per Socket's analysis, the group had previously targeted Checkmarx infrastructure in March 2026, along with Trivy and LiteLLM, suggesting an ongoing campaign against security tooling vendors.
Table 1 shows the attributes of the attack.
| Attribute | Detail |
| Package | @bitwarden/[email protected] |
| Trigger | preinstall lifecycle script |
| Runtime | Bun v1.3.13 (downloaded during install) |
| C2 server | audit.checkmarx[.]cx:443 (94.154.172[.]43) |
| C2 path | /v1/telemetry |
| Fallback C2 | Dynamic, fetched via GitHub Search API dead drop |
| Exfiltration | HTTPS POST (encrypted) + GitHub public repos |
| Attribution | TeamPCP (@pcpcats) |
Table 1. Attributes of the attack.
The package.json provides two execution paths for the malicious script, as shown in Figure 1.

The preinstall hook runs automatically during npm install. The bin field registers bw_setup.js as the bw command, symlinking it into the user's PATH.
Since the legitimate Bitwarden CLI also uses bw as its binary name, this serves as a secondary trigger. Even if preinstall is blocked (e.g., via --ignore-scripts), the malware executes the next time the user or any script invokes bw. The shebang line #!/usr/bin/env node at the top of bw_setup.js ensures it runs as a Node.js script when called directly.
The bootstrap script performs three actions:
A custom ZIP extraction implementation is included to avoid any dependencies, making the bootstrap entirely self-contained.
The payload is an approximately 10 MB single-line JavaScript file containing approximately 285,000 lines when formatted. It bundles legitimate software developer kits (SDKs) (e.g., AWS SDK, Google Cloud client libraries, Azure Identity, Octokit, jsonwebtoken, tar) alongside the malicious orchestration code.
The code employs multiple layers of obfuscation:

The 128-character ASCII set is shuffled deterministically, producing a substitution table where an index with the hex value 0x42 maps to the ASCII character a, 0x6e to u. For example, the C2 domain is stored as [0x42, 0x6e, 0x36, 0x4b, 0x2b, 0x5c, 0xd, 0x57, 0x0, 0xd, 0x7, 0x26, 0x42, 0x3, 0x2a, 0x5c, 0xd, 0x2a], which decodes to an ASCII string for the domain audit.checkmarx[.]cx.
The payload deploys multiple provider classes, each targeting a different credential source. Every provider scans its results with regex patterns to extract npm and GitHub tokens, as shown in Figure 3.

Reads sensitive files from the developer's workstation, with per-OS path lists decoded via the scrambler as shown below in Table 2.
| Platform | Targeted Files |
| Linux | ~/.ssh/id_*, ~/.ssh/keys, .git/config, ~/.npmrc, .npmrc, .env, ~/.claude/mcp.json, ~/.claude.json, ~/.kiro/settings/mcp.json |
| macOS | ~/.aws/credentials, .git/config, ~/.npmrc, .npmrc, .env, ~/.claude.json, .claude.json, ~/.kiro/settings/mcp.json, .kiro/settings/mcp.json |
| Windows | Credential store paths, config.ini |
Table 2. OS path lists from the malware.
Files larger than approximately 5 MB are skipped. All others are read in full and included in the exfiltration payload.
Runs gh auth token via execSync to capture the GitHub CLI's active token, and then harvests the full process.env environment block. Both are returned for token-regex scanning and exfiltration. Unlike the cloud providers below, the Shell Provider does not use any SDK — it relies solely on the single shell command and environment variables.
Detects CI/CD environments via process.env.GITHUB_ACTIONS === "true" and extracts all configured secrets by parsing {"value":"...","isSecret":true} patterns from the Actions runner context.
Uses the bundled AWS SDK (SSMClient, DescribeParameters, GetParameter) to enumerate and read parameters from AWS Systems Manager Parameter Store.
Uses Azure Identity and Key Vault SDK to call getSecretsFromVault, listing and reading secrets from all accessible vaults.
Uses the Google Cloud Secret Manager client to listSecrets and accessSecretVersion, reading all accessible secrets across projects.
All harvested credentials are processed through a two-tier exfiltration system.
Using stolen GitHub tokens, the malware:
This dual exfiltration ensures data reaches the attacker even if the C2 domain is blocked.
When the malware obtains a GitHub token with repo and workflow scopes, class u_ validates the scopes and then:

This is the self-replicating component. When the malware finds an npm token (npm_...):
Calls the npm tokens API with Authorization: Bearer <token>, verifies the token has publish (write) permission on the package scope and that it bypasses two-factor authentication (2FA).
Using the validated token:
For each target package:
Writes a .npmrc with the stolen token's auth line (//registry.npmjs[.]org/:_authToken=<token>) and runs bun publish <tarball>.
Every newly infected package will trigger the same preinstall chain on any developer who installs it, creating an exponential propagation vector.
The malware implements a fall back mechanism for C2 resilience using GitHub's public search API as a covert command channel.
If the primary C2 server (audit.checkmarx[.]cx) is unreachable, the malware:
At the time of analysis, the dead drop contained a placeholder value (hxxps[:]//example[.]com), indicating it was pre-staged for future use. The attacker can rotate C2 infrastructure at any time by creating a new signed commit on any public GitHub repository.
A second fall back (Hr function) searches for commits matching LongLiveTheResistanceAgainstMachines:<base64> to recover previously stolen GitHub tokens from exfiltration repos, bootstrapping new GitHub-based exfiltration channels without any attacker-controlled infrastructure.
Unit 42 has witnessed a shift since the September 2025 Shai-Hulud incident, proving that it wasn’t a temporary spike but the new baseline for software supply chain risk. In an ecosystem where code is shared at the speed of thought, a single compromised dependency can trigger a global cascade.
Ultimately, npm compromises share commonalities and organizations can navigate this volatility by keeping particular best practices in mind. As we continue to monitor, analyze and update our findings related to npm packages, we encourage you to move beyond static defenses and embrace a culture of continuous verification. The supply chain may be the new primary target, but with collective intelligence and relentless visibility, it doesn’t have to be the primary vulnerability.
Palo Alto Networks customers are better protected by our products, as listed below. We will update this threat brief as more relevant information becomes available.
Implement a policy (via a private registry or proxy like Artifactory) that blocks any package version published within the last 24 to 72 hours. Most malicious packages are identified and removed from the public registry within this window.
Many compromises rely on preinstall or postinstall hooks to exfiltrate secrets. Use the following in your .npmrc: ignore-scripts=true.
Use package-lock.json and ensure your CI/CD pipelines use npm ci instead of npm install. This prevents the "hidden" update of dependencies during a build.
Never allow developer machines or CI runners to talk directly to registry.npmjs[.]org. Route all traffic through a private registry.
Attackers often publish packages with the same name as your internal libraries to the public registry. Always use scoped packages (e.g., @myorg/internal-lib) and configure your private registry to only resolve that scope internally.
Verify the OpenID Connect Attestation. Many major packages provide "provenance," proving the code was built on a specific GitHub/GitLab runner. Use tools like slsa-verifier to check these during the build.
Most npm-based malware attempts to send ~/.npmrc tokens or ~/.ssh keys to a C2 server. Apply strict egress network policies to your CI runners. Only allow connections to your private registry and known deployment targets.
Automatically generate an SBOM for every production release. This allows your security team to perform instant impact analysis when a new zero-day is announced.
Palo Alto Networks customers can leverage a variety of product protections and updates to identify and defend against this threat.
If you think you might have been compromised or have an urgent matter, get in touch with the Unit 42 Incident Response team or call:
The Advanced WildFire machine-learning models and analysis techniques have been reviewed and updated in light of indicators associated with npm compromises, including the malicious Bitwarden package.
Advanced URL Filtering and Advanced DNS Security identify known domains and IP addresses associated with this activity as malicious.
Cortex Cloud’s Application Security Module (ASPM) supports the scanning of npm packages installed on cloud resources as well as monitoring audit logs from third party SaaS vendors, including GitHub as discussed within this article. Cortex Cloud prioritizes alerts, issues, policies and assets based on ingested applications as well as their usage. This allows security teams to maintain security awareness across their on-premises and cloud environment by identifying and remediating impacted cloud resources and actively responding to associated runtime operations from the threats discussed within this article through Cortex Cloud’s XDR Agent and serverless operations.
Table 3 lists the network indicators from this activity.
| Indicator | Type |
| audit.checkmarx[.]cx | C2 domain |
| 94.154.172[.]43 | C2 IP address |
| checkmarx[.]cx | Attacker-controlled domain |
| 91.195.240[.]123 | Attacker IP address |
Table 3. Network indicators.
Table 4 lists the GitHub indicators from this activity.
| Indicator | Type |
| helloworm00/hello-world | Dead drop repository |
| bc544f455d7c06c8a1f3446160a6d9a4a8236b11 | Dead drop commit SHA1 hash |
| helloworm00@proton[.]me | Attacker email address |
| Commit messages matching LongLiveTheResistanceAgainstMachines:* | Exfiltration staging |
| Public repositories named <dune-word>-<dune-word>-<3digits> with description "Checkmarx Configuration Storage" | Exfiltration repositories |
Table 4. GitHub indicators.
Table 5 lists the file and process indicators from this activity.
| Indicator | Type | SHA256 hash |
| bw_setup.js | Bootstrap script | f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d |
| bw1.js | Obfuscated payload | 18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb |
| package.json | Malicious manifest | 167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad |
| setup.mjs in infected packages | Worm payload | |
| Unexpected bun process execution | Runtime indicator | |
| .github/workflows/format-check.yml on transient branches | Workflow injection | |
| format-results workflow artifact | Secret exfiltration |
Table 5. File and process indicators.
Table 6 lists the npm indicators from this activity.
| Indicator | Type |
| @bitwarden/[email protected] | Malicious package |
| New preinstall: "node setup.mjs" in package.json | Injected hook |
Table 6. npm indicators.