The Evolution of Supply Chain Attacks
The 2020 SolarWinds incident and the 2021 Log4Shell vulnerability established software supply chain attacks as an A-tier threat category. The response from the security community β SBOM mandates, package signing initiatives, Sigstore, SLSA frameworks β has been substantial. But while defenders were building ecosystem-specific controls, attackers evolved their targeting strategy.
The new model is coordinated multi-ecosystem targeting. Rather than finding a single high-impact package to compromise, sophisticated threat actors register hundreds of packages across npm, PyPI, RubyGems, and NuGet that collectively target a specific organisation's technology stack. These campaigns use shared command-and-control infrastructure, similar payload structures, and overlapping registration patterns β but land in different ecosystems where different SCA tools are watching.
The detection gap: An organisation using separate SCA tools for JavaScript and Python dependencies will see each malicious package in isolation β too low-signal to alert on individually. Correlating across ecosystems reveals the pattern. Most organisations do not do this correlation.
How Coordinated Campaigns Work
The infrastructure behind coordinated supply chain campaigns shares several tell-tale characteristics. Packages across different ecosystems will be registered from the same IP ranges or ASNs, use identical or structurally similar payload scripts, and beacon to the same C2 domain using different communication channels (HTTP, DNS, WebSocket).
The targeting intelligence is gathered from public sources: GitHub organisation members list their languages and projects; job postings reveal technology stacks; open-source repositories expose direct and transitive dependencies. A threat actor profiling a target organisation can identify their top five npm packages, top three Python packages, and three to four GitHub Actions workflows from publicly available information alone.
Typosquatting at Scale
For high-value targets, attackers register multiple typosquats of each critical dependency across every relevant ecosystem. request becomes requests-security in npm (already a legitimate package but illustrative of the pattern) and reqquests in PyPI. The attack succeeds if any single package lands in any single CI run β after which the beacon phone-home delivers the exfiltrated credentials to the attacker regardless of which ecosystem was the entry point.
Install-Time Execution Techniques
Most multi-ecosystem campaigns execute malicious code at install time rather than at import time. This is more reliable because install-time hooks run even if the package is never imported β a developer's npm install or pip install is sufficient to trigger the payload, regardless of whether they actually use the package.
- npm/yarn:
preinstall,install, andpostinstallscripts inpackage.jsonrun automatically during installation. No import required. - Python/pip:
setup.pyarbitrary code execution at install time. Even with PEP 517 builds,pyproject.tomlwith a build backend allows code execution during the build step. - Docker Hub:
RUNinstructions in a pulled base image execute during the build step. A compromised or malicious base image executes code in every derived image build. - GitHub Actions: Third-party actions execute in the CI runner context with access to all workflow environment variables, including repository secrets.
The install-time gap: Your runtime security controls β WAF, EDR, network monitoring β are not active during npm install in a CI runner. The payload executes, exfiltrates CI secrets, and completes before any alert fires. By the time you notice, the credentials are already in the attacker's hands.
GitHub Actions as a Vector
GitHub Actions workflow files reference external actions by repository path and ref β typically a tag or SHA. Mutable tags (e.g., uses: actions/checkout@v4) can be silently updated to point to a different commit. An attacker who compromises the repository hosting a widely-used action can update the tag to point to malicious code; every workflow that runs after the tag update executes the attacker's code in every consuming repository's CI runner.
The 2025 tj-actions/changed-files compromise demonstrated this at scale: a single maintainer account compromise allowed an attacker to modify a tag referenced by over 23,000 repositories, exfiltrating CI secrets from every workflow run until the incident was detected. The multi-ecosystem angle here is that GitHub Actions run in contexts that have access to npm, PyPI, Docker, and cloud provider credentials simultaneously β a single Actions compromise is a multi-ecosystem credential harvest by definition.
Cross-Ecosystem Detection
Effective detection requires correlating signals across ecosystems rather than treating each independently:
- Registration infrastructure correlation: Track the registration details (email domain, IP, GitHub account) of newly installed packages across all ecosystems. Packages registered from the same infrastructure as known-bad packages should be flagged immediately.
- Payload similarity scoring: Apply behavioural similarity analysis to install-time scripts across ecosystems. Scripts that perform the same sequence of operations (environment variable enumeration β HTTP beacon β credential exfiltration) from different packages are likely from the same campaign.
- Unexpected package introduction velocity: A package that went from zero to your lockfile in the same week a similar-looking package appeared in another ecosystem deserves review.
- CI network egress monitoring: Log all outbound connections from CI runners. Any connection to a domain not in your expected allow-list during the dependency installation phase is a high-priority alert.
Defence-in-Depth Controls
- Pin every dependency to exact version and hash: Lockfiles are necessary but not sufficient. Verify the content hash of installed packages, not just their version. npm supports
npm ciwith integrity checks; pip supports--require-hashes. - Pin GitHub Actions to full commit SHAs: Use
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683instead of@v4. A pinned SHA cannot be updated without you explicitly changing it. - Separate CI network namespaces: Run dependency installation steps in network-restricted environments where only your internal package mirror is reachable. Outbound HTTP during install should fail by default.
- Use a private package mirror: Mirror your approved dependency set to an internal registry. Block direct public registry access from CI. Every new package requires explicit approval before it enters the mirror.
- Enable OIDC-based short-lived tokens in CI: Replace long-lived CI secrets with OIDC tokens that expire after the job. Even if the token is exfiltrated, it is useless after the workflow run completes.
- Cross-ecosystem SBOM correlation: Generate SBOMs for all ecosystems in use and feed them into a unified vulnerability and suspicious-package analysis pipeline. Siloed per-ecosystem SCA misses the multi-ecosystem pattern entirely.
The supply chain threat has moved faster than most SCA tooling. Per-ecosystem scanning catches commodity attacks. Defeating coordinated multi-ecosystem campaigns requires unified visibility across all package managers, CI systems, and container registries your organisation uses β treated as a single attack surface, not a collection of independent ones.