Anatomy of a Supply Chain Compromise: How a Poisoned Build Dependency Persisted for 23 Days
A compromised npm package silently exfiltrated CI/CD environment variables — including cloud credentials and signing keys — for 23 days before detection. Here is the complete attack chain and where it could have been stopped.
This analysis is a reconstructed case study of a software supply chain attack pattern observed across multiple incidents in early 2026. Specific identifying details have been changed. The attack chain, exfiltration mechanism, and detection gap analysis are accurate representations of real incident characteristics.
Stage 1 — Initial Compromise of the Package Maintainer
The attack began with the compromise of a widely-used build tooling package on npm. The package had 2.3 million weekly downloads and was used as a devDependency in the affected organisation's CI pipeline. The package maintainer's npm account was compromised via credential stuffing — the maintainer reused a password that had appeared in a prior breach. npm accounts without MFA enabled have no second factor to block this.
Within 4 hours of account takeover, the attacker published a new minor version (2.4.1 → 2.4.2) of the package. The published version was functionally identical to 2.4.1 with one addition: a post-install script that exfiltrated environment variables to an attacker-controlled endpoint.
- Attack vector: credential stuffing on npm account (no MFA)
- Time from account takeover to malicious publish: 4 hours
- Package version bump: minor (2.4.1 → 2.4.2) — passes automated semver update policies
- Exfiltration target: environment variables captured in npm post-install hook
- Exfiltration method: HTTPS POST to attacker-controlled domain (registered 6 days prior)
Stage 2 — Propagation via Automated Dependency Updates
The affected organisation used Renovate Bot to automatically merge minor and patch version bumps for devDependencies. The malicious 2.4.2 version qualified as a minor bump and was automatically merged into the main branch within 18 minutes of publication. The CI pipeline ran on the next commit — 2.4 hours after the merge.
This is the critical architectural fact: the automated dependency update pipeline created a path from an npm account compromise to code execution in the organisation's CI environment with no human approval step.
Stage 3 — Exfiltration (Days 1–23)
The post-install script executed on every CI pipeline run. Each run exfiltrated the complete set of environment variables available during the build phase. These included: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY with EC2 and S3 permissions, a code signing certificate private key, a Docker Hub push token, and Slack webhook URLs.
The exfiltration HTTP request was designed to appear legitimate: it used a TLS-encrypted HTTPS connection, the destination domain resolved to a CDN IP, the User-Agent was set to a common npm tool, and the payload size was kept under 4KB to avoid anomaly detection thresholds.
Detection Gap: 23 Days
The compromise was not detected until Day 23, when a threat intelligence feed flagged the exfiltration domain as a newly registered domain used in infrastructure linked to a known threat actor. The flag triggered a SIEM rule that correlated outbound connections to newly-registered domains against CI runner IPs — a rule added three months earlier after a similar industry incident.
Without that specific SIEM rule, there was no other detection in place: no SCA scanning of post-install scripts, no egress filtering on CI runner network traffic, no alerting on new outbound HTTPS destinations from build infrastructure.
Where Six Controls Would Have Stopped This
| Stage | Attack action | Missing control | Recommended control |
|---|---|---|---|
| Stage 1 | npm account credential stuffing | No MFA on npm account | Enforce MFA on all package registry accounts; monitor for credential exposure in breach feeds |
| Stage 2 | Automated minor-version merge | Auto-merge included devDependencies with post-install scripts | Restrict auto-merge to lockfile-committed, checksum-verified packages; require human review for post-install script additions |
| Stage 2 | Malicious package published | No SCA scan on post-install script content | Run SCA tooling that flags packages with newly-added post-install/pre-install scripts on version bumps |
| Stage 3 | Environment variable exfiltration | No egress filtering on CI runners | Enforce deny-by-default egress policy on all CI runner networks; allowlist only required external endpoints |
| Stage 3 | HTTPS to attacker domain | No newly-registered domain detection | SIEM rule: alert on outbound connections from CI/CD infrastructure to domains registered <30 days |
| Stage 3 | Credential use post-exfiltration | No AWS credential anomaly detection | Enable CloudTrail + GuardDuty; alert on API calls from IPs not in organisational IP ranges |