Threat Modeling

Threat Modeling the Software Supply Chain

Traditional threat modeling focuses on your code. Supply chain threat modeling extends to every tool, dependency, and process that touches your software. Here is how to do it systematically.

Nayan Dey
DevSecOps Architect
8 min read

Threat modeling is a structured process for identifying security threats, understanding their impact, and designing countermeasures. It is standard practice for application security. You diagram your system, identify trust boundaries, enumerate threats using frameworks like STRIDE or PASTA, and prioritize mitigations.

What most teams do not do is apply threat modeling to their software supply chain. The supply chain is treated as a trusted input -- dependencies are assumed to be benign, build tools are assumed to be honest, and registries are assumed to be reliable. SolarWinds, Codecov, the ua-parser-js hijacking, and hundreds of other incidents have proven these assumptions wrong.

Threat modeling the software supply chain requires extending your analysis beyond the code you write to include every external input, tool, and process that shapes the software you deploy.

Defining the Supply Chain Attack Surface

Before you can model threats, you need to define what your supply chain includes. It is broader than most teams realize:

Source code inputs:

  • Open source dependencies (direct and transitive)
  • Internal shared libraries
  • Code snippets copied from Stack Overflow, blog posts, or AI assistants
  • Configuration files and IaC templates sourced externally

Build process inputs:

  • Compilers and interpreters
  • Build tools (Make, Gradle, Webpack, etc.)
  • CI/CD platform (GitHub Actions, Jenkins, GitLab CI, etc.)
  • CI/CD action/plugin ecosystem (third-party GitHub Actions, Jenkins plugins)
  • Container base images
  • Build caches (local and remote)

Distribution and deployment:

  • Package registries (npm, PyPI, Maven Central, etc.)
  • Container registries (ECR, ACR, GCR, Docker Hub)
  • CDN and download infrastructure
  • Update mechanisms (auto-update, package managers)
  • Signing keys and certificates

Development environment:

  • IDE plugins and extensions
  • Developer workstation configurations
  • Pre-commit hooks and local tools
  • AI code assistants and their training data

Each of these is a potential entry point for supply chain compromise.

Applying STRIDE to Supply Chain Components

STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege) is a useful framework for systematically enumerating threats. Here is how it applies to supply chain components:

Package Registries

Spoofing. Dependency confusion attacks spoof internal package names by publishing malicious packages with the same name to public registries. Typosquatting spoofs popular package names with slight misspellings.

Tampering. A compromised maintainer account allows an attacker to publish tampered versions of legitimate packages. Registry infrastructure compromise could serve modified packages transparently.

Information Disclosure. Registry metadata reveals your dependency graph. If your CI/CD downloads dependencies from a public registry, an observer can infer what technologies you use.

Denial of Service. Registry downtime prevents builds from completing. The left-pad incident demonstrated how removing a single package can cascade across the ecosystem.

CI/CD Pipelines

Spoofing. A malicious PR can trigger CI/CD workflows that masquerade as legitimate builds. If your CI/CD does not verify the identity of the triggering event, an attacker can inject code through a forked repository.

Tampering. Build pipeline configuration (YAML files, Jenkinsfiles) are code that can be modified. An attacker who can modify your CI/CD configuration can inject arbitrary steps into your build process.

Elevation of Privilege. CI/CD pipelines typically have access to secrets (deployment credentials, signing keys, API tokens). A compromised build step can exfiltrate these secrets. GitHub Actions, for example, makes repository secrets available as environment variables during workflow execution.

Container Base Images

Spoofing. Pulling node:18 without pinning to a specific digest means you trust Docker Hub to serve the legitimate image. A compromised registry or man-in-the-middle could serve a different image.

Tampering. Base images are updated regularly. If you use mutable tags (:latest, :18), the image you pull today is different from the one you pulled yesterday. This is by design, but it means your build inputs are not deterministic.

Information Disclosure. Container images often include more software than necessary. A base image with curl, wget, and a shell provides tools an attacker can use if they gain execution inside the container.

Building a Supply Chain Threat Model

Step 1: Diagram the Supply Chain

Create a data flow diagram that captures how code and artifacts move from source to production. Include:

  • Where source code comes from (version control, forks, vendored copies)
  • Where dependencies come from (registries, mirrors, vendored copies)
  • What tools process the code (compilers, bundlers, linters, scanners)
  • Where build artifacts go (registries, CDNs, deployment targets)
  • What credentials are used at each step
  • What trust boundaries exist between components

This diagram is often more complex than the application's architecture diagram. That complexity is the point -- it reveals attack surface that is otherwise invisible.

Step 2: Identify Threat Actors

Different threat actors target different parts of the supply chain:

Nation-state actors: Target build infrastructure and widely-used dependencies for broad access. SolarWinds and the 3CX attack are examples. They have the resources for long-duration, stealthy compromises.

Cybercriminals: Target package registries and popular libraries for financial gain (cryptominers, ransomware staging). They favor volume over stealth.

Disgruntled insiders: Target build pipelines and internal libraries they have access to. The sabotage of the colors.js and faker.js libraries by their maintainer is an example of insider threat in open source.

Opportunistic attackers: Exploit misconfigurations (public CI/CD secrets, unprotected registries) and known vulnerabilities in unpatched dependencies. Low sophistication but high volume.

Step 3: Enumerate Threats Systematically

For each component in your supply chain diagram, walk through STRIDE (or your preferred framework) and document specific threats. Be concrete:

Do not write "attacker could compromise a dependency." Instead write "an attacker could publish a typosquatted package named lodassh (misspelling of lodash) to npm. If a developer makes a typo in package.json, the malicious package would be installed and could exfiltrate environment variables during postinstall."

Specific threats lead to specific, testable mitigations.

Step 4: Assess Risk and Prioritize

For each identified threat, assess:

  • Likelihood: How feasible is this attack given the threat actor's capabilities and your current defenses?
  • Impact: What would be compromised if this attack succeeded? Data? Credentials? Customer systems?
  • Detectability: Would you detect this attack with your current monitoring? How quickly?

Prioritize mitigations for high-likelihood, high-impact, low-detectability threats. These are your blind spots.

Step 5: Design Mitigations

Map threats to concrete, implementable mitigations:

| Threat | Mitigation | |--------|-----------| | Dependency confusion | Configure scoped registries, use namespace prefixes for internal packages | | Compromised maintainer account | Pin dependencies with integrity hashes, use lock files, verify signatures | | CI/CD secret exfiltration | Scope secrets to specific workflows, use OIDC instead of static credentials | | Malicious base images | Pin images by digest, use signed images, scan images before deployment | | Build cache poisoning | Use content-addressable caches with integrity verification |

Step 6: Validate and Iterate

Threat models are living documents. Review them when:

  • You add new dependencies or build tools
  • Your CI/CD architecture changes
  • A supply chain attack occurs in the ecosystem (even if it does not affect you directly)
  • Your organization's risk tolerance changes (e.g., after a compliance audit)

Common Blind Spots

Developer workstations. Most threat models start at the version control system. But code is written on developer laptops with dozens of IDE extensions, local tools, and network connections. A compromised VS Code extension can inject code before it ever reaches version control.

AI code assistants. GitHub Copilot, ChatGPT, and similar tools inject code suggestions that bypass traditional code review. If the model suggests a vulnerable pattern or a dependency with a known issue, it enters the codebase through a trusted channel.

Build-time code generation. Annotation processors, code generators, and macro systems run during the build and produce code that is not in version control. A compromised code generator can inject malicious code that does not appear in any PR diff.

Update mechanisms. How your software reaches end users is part of the supply chain. If your auto-update mechanism does not verify signatures, an attacker who compromises your CDN can serve malicious updates.

How Safeguard.sh Helps

Safeguard.sh supports supply chain threat modeling by providing comprehensive visibility into your actual supply chain -- not the one you think you have, but the one that exists in your build artifacts. The platform maps your full dependency graph (direct and transitive), identifies the registries and sources each component comes from, and flags components that match known threat patterns (typosquatting candidates, recently transferred package ownership, abandoned maintainers). This intelligence feeds directly into your threat model, ensuring it reflects your real attack surface rather than an idealized version.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.