Supply chain attacks on open source software are not a single category of threat. They are a family of distinct techniques, each with different prerequisites, different detection characteristics, and different defensive controls. Lumping them all together as "supply chain attacks" is like calling every network intrusion a "hack." It is technically accurate and operationally useless.
A useful taxonomy helps defenders think clearly about which attacks they are exposed to, which controls address which attack types, and where their defensive gaps are. Here is one.
Category 1: Source Code Compromise
The attacker introduces malicious code into a project's source repository. This can happen through several mechanisms:
Compromised maintainer accounts. The attacker gains access to a maintainer's account on the source hosting platform (GitHub, GitLab) and pushes malicious commits directly. This is the most direct form of source compromise.
Example: The PHP project's git server was compromised in 2021 when attackers pushed malicious commits disguised as typo fixes under the names of known developers. The backdoor code, if it had been released, would have allowed remote code execution on any PHP server.
Social engineering of maintainers. The attacker convinces a maintainer to merge a malicious contribution through seemingly legitimate pull requests. This can involve long-term trust building, where the attacker makes genuine contributions for months or years before introducing malicious code.
Example: The xz Utils backdoor (2024) involved a contributor who spent approximately two years building trust with the project before introducing a sophisticated backdoor targeting OpenSSH authentication.
Compromised CI/CD. The attacker compromises the project's build or CI/CD system rather than the source code itself. Build scripts, CI configuration files, and GitHub Actions workflows can be modified to inject malicious code during the build process without altering the source.
Example: The Codecov breach (2021) exploited a compromised CI tool to exfiltrate environment variables (including secrets) from thousands of repositories that used Codecov's bash uploader.
Defensive controls: Code review policies, signed commits, branch protection rules, CI/CD hardening, and maintainer account 2FA.
Category 2: Build and Distribution Compromise
The attacker does not modify source code. Instead, they compromise the build or distribution process so that the distributed artifact contains malicious code that does not appear in the source repository.
Compromised build environments. The build server or container is modified to inject malicious code during compilation. The source repository looks clean. The published binary contains a backdoor.
Example: The SolarWinds attack (2020) injected malicious code during the build process. The source code was clean. The build system was compromised.
Registry and mirror compromise. The attacker compromises a package registry or distribution mirror and replaces legitimate packages with malicious versions.
Example: This has been demonstrated against multiple registries, including npm (via credential theft) and Linux distribution mirrors (via mirror hijacking).
Typosquatting and name confusion. The attacker registers package names that are visually similar to popular packages. Users who mistype or misread package names install the malicious version.
Example: Hundreds of typosquatting packages have been discovered on PyPI, npm, and RubyGems. Packages like crossenv (targeting cross-env) and python3-dateutil (targeting python-dateutil) are well-documented examples.
Dependency confusion. The attacker publishes a public package with the same name as a private internal package. Build systems that check public registries before private ones pull the attacker's package.
Example: Alex Birsan's 2021 research demonstrated dependency confusion attacks against Apple, Microsoft, and PayPal, achieving code execution on internal build systems.
Defensive controls: Reproducible builds, signed artifacts, registry namespace controls, dependency pinning, hash verification, and private registry configuration.
Category 3: Dependency Manipulation
The attacker exploits the dependency resolution mechanism to force installation of malicious code without modifying the target project itself.
Transitive dependency poisoning. The attacker compromises a deep transitive dependency, several levels removed from the target project. The target project's direct dependencies are clean, but a dependency of a dependency of a dependency contains the payload.
Example: The event-stream attack (2018) targeted a direct dependency (flatmap-stream) of the event-stream package. Applications that depended on event-stream got the malicious transitive dependency automatically.
Version constraint exploitation. The attacker publishes a new version of a package that satisfies the version constraints of downstream consumers. If those consumers use floating version ranges (^1.0.0, ~2.3.0), their next build will pull the malicious version.
Dependency hijacking. The attacker takes ownership of an abandoned or transferred package name and publishes new versions under the existing name. Consumers who trust the package based on its history unknowingly pull the attacker's code.
Defensive controls: Lock files, hash verification, dependency auditing, version pinning, and monitoring for package ownership transfers.
Category 4: Maintainer Actions
The attacker is the maintainer, or was the maintainer before the attack. This category includes both malicious maintainers and compromised maintainers.
Intentional sabotage. The maintainer deliberately introduces destructive or malicious code. This may be motivated by ideology, protest, financial gain, or coercion.
Example: The colors/faker incident (2022) where the maintainer deliberately corrupted widely-used packages in protest.
Maintainer coercion. The maintainer is pressured or threatened into introducing malicious code. State-sponsored actors may target maintainers of critical infrastructure projects.
Trust decay. A legitimate maintainer gradually becomes unreliable or malicious over time. This is extremely difficult to detect because the transition is gradual and occurs within a trusted context.
Defensive controls: Multi-maintainer review requirements, automated behavioral analysis, contributor activity monitoring, and governance structures that prevent single-person control over releases.
Category 5: Metadata and Configuration Attacks
The attacker does not modify executable code but manipulates package metadata or configuration to achieve malicious outcomes.
Manifest manipulation. The attacker modifies package manifests (package.json, setup.py, pom.xml) to add malicious dependencies, change dependency versions, or alter install scripts. The library code itself may be untouched.
Install script exploitation. Many package managers execute scripts during installation (npm postinstall, Python setup.py). These scripts run with the user's permissions and can perform arbitrary actions.
Configuration injection. The attacker modifies configuration files (.npmrc, pip.conf, settings.xml) to redirect dependency resolution to attacker-controlled registries.
Defensive controls: Manifest validation, install script auditing, configuration file integrity monitoring, and registry allowlisting.
Mapping Controls to Categories
No single control addresses all categories. Effective supply chain security requires layered defenses:
| Control | Source | Build/Dist | Dependency | Maintainer | Metadata | |---------|--------|------------|------------|------------|----------| | Code review | Strong | - | - | Moderate | Moderate | | Signed releases | - | Strong | - | - | - | | Lock files | - | - | Strong | - | Moderate | | 2FA on accounts | Strong | - | - | Strong | - | | Reproducible builds | - | Strong | - | - | - | | Dependency auditing | - | - | Strong | Moderate | Strong | | SBOM generation | Moderate | Moderate | Strong | Moderate | Strong |
The table makes clear that organizations need multiple controls operating simultaneously. Relying on any single control leaves entire attack categories unaddressed.
How Safeguard.sh Helps
Safeguard.sh provides defense across the full taxonomy of supply chain attacks. Our SBOM generation creates complete dependency inventories that detect transitive dependency changes. Our continuous monitoring watches for package ownership transfers, unexpected version changes, and known malicious indicators across every package in your dependency tree. When a supply chain attack is discovered anywhere in the ecosystem, Safeguard.sh maps the impact to your specific projects, telling you exactly which applications are affected and what needs to be remediated. We do not just tell you about supply chain attacks. We tell you which ones affect you.