Threat Research Center High Profile Threats Malware Malware The npm Threat Landscape: Attack Surface and Mitigations 12 min read Related Products Advanced DNS Security Advanced URL Filtering Cloud-Delivered Security Services Cortex Cortex Cloud Unit 42 Incident Response By: Unit 42 Published: April 24, 2026 Categories: High Profile Threats Malware Tags: Credential Harvesting GitHub Npm packages Obfuscation Payload Supply chain Worm propagation Share Executive Summary 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 New Baseline for npm Threats 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: Wormable propagation: Malicious payloads now prioritize the theft of npm tokens and GitHub Personal Access Tokens (PATs) to automatically infect and republish legitimate packages, as seen in the March 2026 Axios compromise . Infrastructure-level persistence: Attackers are no longer just stealing data; they are embedding themselves into continuous integration/continuous delivery (CI/CD) pipelines to attain long-term, undetectable access to enterprise environments. Multi-stage payloads: Following the September 2025 template, current attacks often deploy dormant “sleeper” dependencies that only activate under specific environmental conditions to evade automated scanners. npm Attacks Seen As a Whole 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: Details of major incidents: Real-time analysis of significant package compromises (e.g., Shai-Hulud 2.0 , Axios , Chalk/Debug ) Cross-campaign correlation: Identifying common infrastructure or code snippets that link disparate attacks to the same threat actors Remediation playbooks: Actionable guidance for rotating credentials and purging malicious dependencies from local and cloud-based caches Shai-Hulud: A New Wave 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: Docker Hub images GitHub Actions VS Code extensions Palo Alto Networks customers are better protected from the threats described in this article through the following products and services: Advanced WildFire Advanced URL Filtering and Advanced DNS Security Cortex Cloud 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. Related Unit 42 Topics Supply Chain , Credential Harvesting , Obfuscation , Backdoor April 2026 - Shai Hulud: A New Wave Broader Campaign Context 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: Docker Hub: Poisoned checkmarx/kics images (v2.1.20, v2.1.21, latest, alpine, debian) GitHub Actions: Malicious checkmarx/ast-github-action v2.3.35 VS Code extensions: Backdoored checkmarx/ast-results (v2.63, v2.66) and checkmarx/cx-dev-assist (v1.17, v1.19) npm: The @bitwarden/cli package analyzed in this report 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. Attack Overview Table 1 shows the attributes of the attack. Attribute Detail Package @bitwarden/cli@2026.4.0 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. Stage 1: Bootstrap - bw_setup.js The package.json provides two execution paths for the malicious script, as shown in Figure 1. Figure 1. Execution paths for the malicious script in the package.json file. 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: Platform detection : Identifies the OS and architecture (Linux, macOS, Windows; x64 or arm64), including musl versus glibc detection on Linux. Bun runtime download : Downloads the Bun JavaScript runtime (v1.3.13) from the official github[.]com/oven-sh/bun releases. This is needed because the main payload uses Bun-specific APIs (shell execution, file I/O, gzip) not available in Node.js. Payload execution : Runs bw1.js using the freshly downloaded Bun binary. A custom ZIP extraction implementation is included to avoid any dependencies, making the bootstrap entirely self-contained. Stage 2: The Payload - bw1.js 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. Obfuscation Techniques The code employs multiple layers of obfuscation: String table rotation: A function _0x214e resolves hex indices to strings from a large rotated array ( _0x1ee1 ), breaking simple static string analysis. Seeded ASCII shuffle cipher: Sensitive strings (domains, file paths, shell commands) are encoded as arrays of numeric indices into a Fisher-Yates-shuffled ASCII table. The shuffle uses a linear congruential PRNG seeded with 0x3039 (12345), as shown below in Figure 2. Figure 2. Seeded ASCII shuffle cipher. 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 . Gzip and Base64 embedded payloads: Several blobs are stored as gzip-compressed Base64 strings, including an RSA public key, a GitHub Actions workflow YAML, the worm's setup.mjs loader and a manifesto string Mangled identifiers: All variable and function names are replaced with hex patterns such as _0x3865d8 Credential Harvesting 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. Figure 3. Regex patterns to extract npm and GitHub tokens. File System Provider (Cn) 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. Shell Provider ( un ) 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. GitHub Actions Provider ( Co ) 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.