Mini Shai-Hulud Targets SAP npm Packages: CI/CD Publishing Pipeline Abused in Supply Chain Attack
Executive Summary
Our research team identified a sophisticated supply chain attack targeting SAP Cloud Application Programming (CAP) framework packages. The campaign demonstrates advanced techniques for compromising trusted publishing pipelines and injecting malicious code directly into enterprise CI/CD workflows.
The activity has been attributed to TeamPCP, a financially motivated threat actor known for large-scale supply chain compromises and cloud-focused post-exploitation campaigns. This attribution is based on shared cryptographic artifacts, overlapping infrastructure, and reuse of obfuscation and encoding routines observed in prior operations.
On April 29, 2026, malicious versions of four SAP-related npm packages were published following unauthorized modifications to a repository release workflow. The attacker leveraged GitHub’s OIDC-based trusted publishing mechanism to extract short-lived npm publish tokens and release compromised packages to the registry.
The payload deploys the Bun JavaScript runtime to execute heavily obfuscated code, likely to evade detection mechanisms focused on Node.js execution patterns.
Affected Packages
The following malicious package versions were published and should be considered compromised:
- @cap-js/[email protected] – Published April 29, 2026 at 11:25 UTC
- @cap-js/[email protected] – Published April 29, 2026 at 11:25 UTC (since unpublished)
- @cap-js/[email protected] – Published April 29, 2026 at 11:25 UTC
- [email protected] – Published April 29, 2026 at 09:55 UTC
Any installations of these versions between 09:55 UTC and 14:00 UTC on April 29, 2026 should be considered compromised. Organizations using SAP CAP framework packages should immediately audit their dependencies and follow the response actions outlined below.
What is SAP
SAP is one of the world’s largest enterprise software companies, providing business management software to over 440,000 customers globally. The SAP Cloud Application Programming (CAP) framework is a development platform for building enterprise applications that integrate with SAP’s business systems. The targeting of CAP packages represents a strategic shift toward enterprise-focused supply chain attacks, as these frameworks are predominantly used in large-scale business environments with access to critical corporate data and systems.
Technical Breakdown
Stage 1: Initial Compromise
The attack begins with two independent, parallel infrastructure compromises executed to obtain publishing credentials through different mechanisms.
Vector A: CircleCI Token Exfiltration
Analysis of CircleCI build logs reveals the first compromise vector. On April 29, a brief draft pull request (#1223) titled “feat: ci speedup” was opened from the account gruposbftechrecruiter/harkonnen-navigator-149. The PR triggered automated CircleCI builds with access to npm publishing tokens through environment variables.
The malicious commit (a959014aa7b7fc37a9b5730c951776e7db2920a6) added a Bun loader at bin/config.mjs, an obfuscated payload at bin/mbt.js, and modified the test command to execute the token exfiltration logic. The PR was closed within minutes and the branch force-pushed to remove evidence, but CircleCI retained the build artifacts that reveal the attack mechanism.
Vector B: GitHub Actions OIDC Abuse
The second vector targeted GitHub Actions runners directly through repository manipulation. The attacker pushed commit 0a3dd44 to the update/releases branch of cap-js/cds-dbs under the spoofed identity [email protected]. This commit modified release-please.yml to execute on the non-standard branch and replaced legitimate publish steps with malicious OIDC token extraction logic.
Stage 2: Malicious Package Publication
With publishing credentials secured through both compromise vectors, the attacker published four malicious packages to the npm registry within a two-hour window. The cloudmtabot token stolen via CircleCI was used to publish [email protected] at 09:55 UTC, while the OIDC token extracted from the GitHub Actions workflow was used to publish the three @cap-js packages at 11:25 UTC.
After publishing, commit eca039d injected persistence files into the cap-js/cds-dbs repository, followed by cleanup commit 4ae7eb0 that removed the OIDC token extraction code in an attempt to hide the attack. The compromised workflow (run 25108178873) was eventually terminated and the branch force-reverted, but the malicious packages had already propagated through the npm registry.
Stage 3: Victim Impact
3.1: Package Installation and Preinstall Execution
When developers or CI/CD systems install any of the compromised packages, the attack triggers immediately through npm’s preinstall hook. Each malicious package contains a package.json entry specifying “preinstall”: “node setup.mjs”, which executes before the package installation completes – meaning the attack succeeds even if the install is later aborted due to dependency conflicts, network issues, or security scanning.
The preinstall hook deploys two files to the target system:
setup.mjs (Dropper): A lightweight JavaScript module responsible for initial system reconnaissance and bootstrapping the main payload.
execution.js (Main Payload): The 11.7MB obfuscated file containing the complete attack toolkit, including credential harvesting logic, exfiltration mechanisms, and persistence installation.
The execution.js file implements a sophisticated three-layer obfuscation scheme to resist analysis. The first layer uses string table obfuscation with approximately 49,000 entries, a custom base64 alphabet, and index rotation to obscure lookup operations. The second layer protects sensitive strings (credential paths, environment variable names, API endpoints) through PBKDF2 key derivation with per-byte SHA256 transformations across roughly 200,000 iterations. The third layer embeds compressed components including GitHub workflow manipulation logic, AI tool configuration injection capabilities, memory extraction scripts, and an RSA public key used for key wrapping during exfiltration.
The dropper completes its work within seconds of npm install, making detection particularly challenging since the malicious activity begins before traditional package scanning or approval processes can intervene.
3.2: Runtime Bootstrap via Bun
Once the preinstall hook fires, setup.mjs performs OS and architecture detection, then downloads Bun 1.3.13 from GitHub releases to execute the main payload. Bun is a high-performance JavaScript runtime designed as a drop-in replacement for Node.js, offering full API compatibility with faster startup and execution times.
The choice of Bun is deliberate – using a non-standard runtime allows the attacker to bypass detection logic that monitors Node.js execution patterns. After launching execution.js through Bun, the dropper cleans up its artifacts and the temporary Bun binary to reduce forensic visibility.
Stage 4: Post-Compromise Operations
4.1: Environment Checks and Evasion
Before initiating malicious activity, the payload performs two environment checks designed to control where it runs and how it persists:
Russian Locale Check: The malware immediately exits with exit(0) if it detects Russian language settings on the host. This guardrail strongly suggests the operators want to avoid prosecution in Russian-speaking jurisdictions, a common pattern among financially motivated threat actors operating from those regions.
CI/CD Environment Detection: The payload checks against 32 known CI platform indicators to determine its execution context. On developer machines, it daemonizes by forking a detached child process while the parent exits, allowing the malware to survive after the npm install command completes. On CI/CD systems, it runs inline to maximize execution time within the pipeline before the runner terminates.
4.2: Credential Harvesting
The malware deploys five parallel credential collectors, each targeting a specific platform or credential type:
- npm tokens: Reads ~/.npmrc and environment variables for npm authentication credentials
- AWS credentials: Extracts standard credential files and performs Secrets Manager dumps via the AWS SDK
- GCP credentials: Harvests credential databases and dumps Secret Manager contents
- Azure credentials: Uses the Azure Identity SDK to extract authentication tokens
- GitHub tokens: Collects standard tokens and dumps GitHub Actions runner memory via /proc/{pid}/mem
In addition to these targeted collectors, a file harvester scans specific paths covering SSH keys, cryptocurrency wallets, VPN profiles, database history files, and AI assistant configuration files. The targeting of AI assistant configurations is particularly notable – files like ~/.claude.json and ~/.kiro/settings/mcp.json define internal tools, APIs, and integrations, exposing an organization’s operational surface far beyond traditional credentials.
4.3: GitHub-Based Exfiltration
Harvested data is encrypted using AES-256-GCM with keys wrapped via RSA-4096-OAEP using the attacker’s embedded public key. The encrypted payload is then uploaded to public GitHub repositories created on the victim’s own account, serving as dead-drops that the attacker can later access. This technique routes exfiltration through legitimate GitHub traffic, making it indistinguishable from normal repository activity in network logs.
4.4: npm Self-Propagation
When the harvested credentials include valid npm tokens, the malware attempts worm-like propagation across additional packages. It first validates each token by checking if it bypasses 2FA requirements and inspecting its scopes. For tokens with sufficient privileges, the malware enumerates all packages owned by the victim’s organization, then republishes them with hand-rolled PUT requests directly to registry.npmjs.org to avoid detection by npm CLI tooling.
This capability could exponentially expand the campaign’s reach by infecting every package the compromised account can publish, creating a self-propagating supply chain worm similar to the original Shai-Hulud incident.
4.5: Workflow Injection and IDE Persistence
The malware establishes persistence through two parallel mechanisms targeting both CI/CD pipelines and developer environments.
GitHub Workflow Injection: For each accessible repository, the malware creates a new branch (using patterns like dependabout/…/setup-formatter) and commits a malicious workflow that exports toJSON(secrets) to a downloadable artifact. After dispatching the workflow, the malware polls for completion to retrieve the dumped GitHub Actions secrets context, capturing the complete set of secrets configured in the repository.
IDE Persistence: The malware commits files to all accessible repositories that trigger execution through development tools when the repository is opened:
- .vscode/tasks.json configured with runOn: folderOpen – executes when VSCode opens the project folder
- .claude/settings.json configured with a SessionStart hook – executes when Claude Code starts a session
- Self-copies of execution.js and setup.mjs placed in the repository for re-infection
This transforms compromised repositories into propagation vectors. Any developer who clones a repository and opens it in VSCode or Claude Code triggers the dropper, even if they never directly install the malicious npm package.
Indicators of Compromise (IOCs)
| Category | Indicator | Notes |
| Malicious Packages | @cap-js/[email protected] | Published 11:25 UTC, April 29, 2026 |
| Malicious Packages | @cap-js/[email protected] | Published 11:25 UTC, April 29, 2026 (since unpublished) |
| Malicious Packages | @cap-js/[email protected] | Published 11:25 UTC, April 29, 2026 |
| Malicious Packages | [email protected] | Published 09:55 UTC, April 29, 2026 via stolen token |
| File Artifacts | setup.mjs | Preinstall dropper script |
| File Artifacts | execution.js | 11.7MB obfuscated payload |
| File Artifacts | bin/config.mjs | Bun loader added via malicious PR |
| File Artifacts | bin/mbt.js | Obfuscated payload in PR attack |
| Persistence Files | .vscode/setup.mjs | IDE integration dropper |
| Persistence Files | .vscode/tasks.json | VSCode task configuration |
| Persistence Files | .claude/settings.json | Claude AI configuration hook |
| Persistence Files | .claude/setup.mjs | Claude AI dropper copy |
| Persistence Files | .claude/execution.js | Claude AI payload copy |
| GitHub Commits | 0a3dd44 | Initial malicious workflow modification |
| GitHub Commits | eca039d | Persistence file injection commit |
| GitHub Commits | 4ae7eb0 | Cleanup commit removing OIDC code |
| GitHub Commits | a959014aa7b7fc37a9b5730c951776e7db2920a6 | CircleCI token exfiltration commit |
| GitHub Identities | [email protected] | Spoofed commit identity (unsigned) |
| GitHub Accounts | gruposbftechrecruiter/harkonnen-navigator-149 | Malicious PR source account |
| GitHub Branches | update/releases | Non-standard branch for workflow abuse |
| GitHub PRs | #1223 in SAP/cloud-mta-build-tool | Token exfiltration PR |
| Email Addresses | [email protected] | Compromised service account |
| Service Accounts | cloudmtabot | Legitimate npm publishing account |
| GitHub Repositories | Repositories with description “A Mini Shai-Hulud has Appeared” | Exfiltration dead drops |
| Workflow Files | release-please.yml | Modified to trigger on non-main branch |
| Process Indicators | Bun runtime execution during npm install | Unusual JavaScript runtime |
| Process Indicators | Forked detached child processes from npm install | Daemonization on developer machines |
| Network Indicators | github.com/oven-sh/bun/releases/download/bun-v1.3.13/ | Bun runtime download |
| Network Indicators | Direct PUT requests to registry.npmjs.org | npm self-propagation bypass |
| GitHub Branches | Branches matching dependabout/…/setup-formatter pattern | Workflow injection branches |
| Behavioral | Russian locale check causing immediate exit | Geographic targeting guardrail |
| Behavioral | Public repository creation on victim accounts | Exfiltration dead-drops |
| Attack Markers | OhNoWhatsGoingOnWithGitHub | String marker in malware |
| Commit Messages | feat: ci speedup | Malicious PR title |
| Commit Messages | fix: ci | Repository compromise commit message |
| File Paths | /proc/{pid}/mem | CI runner memory extraction target |
| Encryption | RSA-4096 public keys | Embedded in payload for key wrapping |
| Encryption | AES-256-GCM | Data encryption for exfiltration |
| Obfuscation | PBKDF2 with salt ctf-scramble-v2 | String protection mechanism |
Immediate Response Actions
Identify Exposure
Audit lockfiles for affected versions installed during the attack window.
Rotate Credentials
Immediately rotate cloud credentials, npm tokens, SSH keys, GitHub tokens, and AI assistant configuration credentials.
Repository Audit
Search for unauthorized workflow changes, suspicious configuration files, and unknown commits modifying CI/CD pipelines.
Package Integrity
Validate for unexpected preinstall scripts, missing provenance attestations, and unusual package size changes.
Detection and Prevention
Organizations should tighten trusted publishing by restricting OIDC usage to specific workflows and branches while avoiding repository-wide trust configurations. Enforce commit verification by requiring signed commits for workflow changes.
Monitor CI behavior to detect unexpected runtime downloads such as Bun and alert on unusual workflow modifications. Track provenance by monitoring for missing or altered SLSA attestations, and maintain runtime visibility to detect execution during install phases and monitor credential access patterns.
The Bigger Picture
This campaign reflects a clear shift in supply chain attacks from package injection to pipeline compromise, from credential theft to token extraction at runtime, and from endpoint malware to developer ecosystem targeting.
The attack also highlights a growing focus on AI-integrated development environments. By targeting AI assistant configuration and MCP tooling, attackers gain visibility into internal systems, APIs, and workflows that extend far beyond traditional credential access.
Rather than relying on social engineering or stolen credentials alone, this operation demonstrates how attackers can directly manipulate trusted automation systems to distribute malicious code at scale. As organizations increasingly rely on automated publishing and AI-assisted development, these systems become part of the attack surface and require the same level of security rigor as production infrastructure.
How Upwind Detects This Attack
Upwind’s runtime security platform identifies this attack chain at multiple points, providing visibility across CI/CD pipelines, developer workstations, and cloud workloads through eBPF-based runtime monitoring.
CI/CD Runtime Visibility: During npm install, Upwind detects the preinstall hook spawning setup.mjs, the subsequent download of the Bun runtime from GitHub releases, and the execution of the obfuscated execution.js payload. These events are correlated to a single build context, distinguishing legitimate package installation from supply chain compromise.
Anomalous Credential Access: The platform monitors access to sensitive files including ~/.npmrc, cloud provider credentials (~/.aws/, ~/.azure/, ~/.config/gcloud/), SSH keys, and AI assistant configurations. The five parallel credential collectors used by this malware produce a distinctive access pattern that triggers high-confidence detections.
Memory Extraction Detection: Upwind detects unauthorized reads of /proc/{pid}/mem targeting GitHub Actions runner processes, catching the in-memory secret extraction technique used to bypass on-disk credential protections.
Egress Anomalies: The platform identifies suspicious outbound connections during package installations, including direct PUT requests to registry.npmjs.org from non-CLI processes (indicating self-propagation attempts) and public repository creation on user accounts that match exfiltration dead-drop patterns.


