Dependency and Supply Chain Security
SBOM, Signing, and Artifact Integrity
TL;DR
Supply chain attacks target the weakest link: dependencies. Mitigate risk through three mechanisms: (1) comprehensive inventory using Software Bill of Materials (SBOM), (2) artifact signing and verification to ensure integrity and provenance, (3) continuous vulnerability scanning and controlled updates. A single compromised dependency can compromise entire applications; treat supply chain security as a critical control.
Learning Objectives
- Understand supply chain attack vectors and real-world incidents
- Create and maintain accurate SBOMs for compliance and risk visibility
- Implement cryptographic signing and verification for build artifacts
- Establish processes for detecting and responding to vulnerable dependencies
- Design governance for third-party code and open-source risks
Motivating Scenario
In 2020, the SolarWinds attack compromised over 18,000 organizations. The attacker gained access to SolarWinds' build system and inserted malicious code into updates distributed to millions of customers. The victims weren't vulnerable due to misconfiguration—they were using the software exactly as intended. The attack succeeded because:
- No visibility into components: Organizations didn't know what code was in SolarWinds binaries
- No integrity verification: Updates weren't cryptographically signed; no way to detect tampering
- Blind trust: Software from trusted vendors was assumed safe without verification
Modern supply chain security requires explicit verification at every stage:
- Knowing what's in your software (SBOM)
- Verifying artifacts haven't been modified (signing)
- Detecting when dependencies become vulnerable (scanning)
Core Concepts
The Supply Chain Attack Surface
Dependencies introduce risk across multiple vectors:
┌─────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────┤
│ Direct Dependencies (explicit) │
├─────────────────────────────────────┤
│ Transitive Dependencies (implicit) │
├─────────────────────────────────────┤
│ Package Repository (PyPI, npm) │
├─────────────────────────────────────┤
│ Maintainer's Development Environment│
├─────────────────────────────────────┤
│ Maintainer's Build System │
└─────────────────────────────────────┘
Each layer can be compromised:
- Typosquatting:
lesstsinstead oflodash - Abandoned packages: Transferred to malicious maintainer
- Compromised credentials: Attacker gains publishing rights
- Build system compromise: Malicious code injected during build
- Transitive dependencies: Vulnerable version required by indirect dependency
Software Bill of Materials (SBOM)
Definition: An SBOM is a formal, machine-readable inventory of all components, libraries, and dependencies in an application, including versions and known vulnerabilities.
SBOM Formats:
| Format | Standard | Use Case | Machine Readable |
|---|---|---|---|
| SPDX | Linux Foundation | Comprehensive, standardized | Yes (JSON, RDF, XML) |
| CycloneDX | OWASP | Vulnerability-focused, lightweight | Yes (JSON, XML) |
| Package URL (purl) | Industry standard | Universal package identification | Yes |
| Custom JSON | Proprietary | Quick internal use | Yes |
Example SBOM Entry (CycloneDX format):
{
"components": [
{
"type": "library",
"name": "lodash",
"version": "4.17.20",
"purl": "pkg:npm/lodash@4.17.20",
"vulnerabilities": [
{
"ref": "CVE-2021-23337",
"severity": "high"
}
}
}
Artifact Signing and Verification
Definition: Cryptographic signing proves an artifact's authenticity and integrity. Only the entity with the private key can create valid signatures; anyone can verify using the public key.
Signing Workflow:
1. Developer creates artifact (binary, container image, package)
2. Sign with private key → Creates cryptographic signature
3. Distribute artifact + signature
4. Recipient verifies signature using public key
5. If valid: Artifact hasn't been modified and is from known source
6. If invalid: Artifact is untrusted, reject it
What Gets Signed:
- Container images (cosign, Notary)
- Package releases (npm, PyPI, Maven)
- Binary executables (Windows code signing)
- Commit history (git GPG signatures)
- Build artifacts
Provenance and Attestations
Definition: Provenance is the complete history of where software came from—who built it, when, with what inputs, and what was the build configuration.
In-toto Framework creates attestations:
Build Attestation:
- Input: source code commit hash
- Builder identity and timestamp
- Build command invoked
- Output: artifact hash
- Signature from builder
Consumers verify the entire chain: source → build → artifact.
Practical Example
Creating and Scanning an SBOM
- Generate SBOM
- Verify and Scan
- CI/CD Integration
# Using Syft (multi-language)
syft scan <image-or-directory> -o json > sbom.json
# Using CycloneDX for Maven projects
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom
# Using pip-audit for Python
pip install pip-audit
pip-audit --desc > sbom.txt
# Using npm for Node.js
npm install -g npm-check-updates
npm audit --json > sbom.json
Generated SBOM output:
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"components": [
{
"type": "library",
"name": "express",
"version": "4.18.1",
"purl": "pkg:npm/express@4.18.1",
"licenses": [{"license": {"name": "MIT"}}]
}
}
# Scan SBOM for known vulnerabilities using Grype
grype sbom:sbom.json --output json > vulnerabilities.json
# Check for specific CVE
grype sbom:sbom.json --only-fixed
# Generate supply chain attestation with cosign
cosign sign-blob --key cosign.key sbom.json > sbom.json.sig
# Verify attestation
cosign verify-blob --key cosign.pub --signature sbom.json.sig sbom.json
Output showing vulnerabilities:
{
"matches": [
{
"vulnerability": {
"id": "CVE-2021-23337",
"dataSource": "https://nvd.nist.gov/vuln/detail/CVE-2021-23337",
"severity": "high",
"description": "..."
},
"artifact": {
"name": "lodash",
"version": "4.17.20"
}
}
}
# GitHub Actions example
name: Security Scan
on: [push, pull_request]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Generate SBOM
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
path: .
format: cyclonedx-json
output-file: sbom.json
# Scan for vulnerabilities
- name: Scan SBOM
uses: anchore/grype-action@v0
with:
sbom: sbom.json
fail-on: high
# Sign artifact
- name: Sign SBOM
run: |
cosign sign-blob \
--key ${{ secrets.COSIGN_KEY }} \
sbom.json > sbom.json.sig
# Upload for compliance
- name: Upload to artifact store
run: |
gsutil cp sbom.json gs://artifacts/${{ github.sha }}/
gsutil cp sbom.json.sig gs://artifacts/${{ github.sha }}/
Defenses and Governance
Dependency Pinning and Lock Files
Prevent unexpected updates that might contain vulnerabilities:
// package-lock.json (npm)
{
"name": "my-app",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/express": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
"integrity": "sha512-...",
"requires": {
"body-parser": "1.20.0"
}
}
}
}
Lock files provide:
- Exact version reproducibility across environments
- Integrity hashes preventing tampering
- Transitive dependency tracking
Vulnerability Scanning in CI/CD
Automatically detect vulnerable dependencies before they reach production:
# Every commit triggers scanning
npm audit --audit-level=moderate
pip install safety && safety check
cargo audit
go list -json -m all | nancy sleuth
Private Package Repositories
Control and audit all dependencies:
# Artifactory, Nexus, or GitHub Package Registry
npm config set registry https://artifacts.company.com/npm/
pip config --user set index-url https://artifacts.company.com/pypi/
Benefits:
- Mirror external repositories with caching
- Scan packages before making available
- Block vulnerable or unlicensed packages
- Audit all access
License Compliance
Track licenses to avoid legal liability:
# SPDX license compliance check
fossology scan my-project/
# or use FOSSA, Black Duck, WhiteSource
When to Use / When Not to Use
- Generate SBOMs for every release and deployment
- Sign all artifacts with keys held in secure vaults
- Require signature verification before using artifacts
- Scan dependencies on every commit automatically
- Pin exact versions in lock files (reproducibility)
- Use private package repositories with scanning
- Maintain approved vendor lists
- Audit and approve major version updates
- Manual dependency tracking (error-prone at scale)
- Trusting package repositories without verification
- No version pinning (unpredictable updates)
- Scanning only at release time (late detection)
- Ignoring transitive dependencies
- Using unvetted community packages
- No attestation or provenance tracking
- Update everything immediately without testing
Patterns and Pitfalls
🎯 Secure Pattern: Automated Scanning
Run vulnerability scanners on every commit, gate PRs on high-severity findings, track trends over time.
⚠️ Pitfall: Manual Reviews
Attempting to manually review each dependency for vulnerabilities is ineffective at scale and easily missed.
🎯 Secure Pattern: Cryptographic Verification
All distributed artifacts signed and verified before use. Prevents tampering anywhere in supply chain.
⚠️ Pitfall: Trust on First Use
Downloading and running code without verification assumes no compromise has occurred (SolarWinds case study).
🎯 Secure Pattern: Lock Files
Commit lock files to source control. Ensures reproducible builds and easy detection of unexpected changes.
⚠️ Pitfall: Floating Versions
Using version ranges (^1.0.0) means different builds get different code. Prevents reproducibility and debugging.
Design Review Checklist
- SBOM generated for every application and release?
- SBOMs in machine-readable format (CycloneDX, SPDX)?
- Transitive dependencies included in SBOM?
- License information captured for compliance?
- Version and source information complete?
- Automated scanning on every commit?
- Severity thresholds configured (gate high/critical)?
- Vulnerability data sources current (NVD, GitHub, etc.)?
- Process documented for responding to new vulnerabilities?
- Update testing required before deployment?
- All release artifacts signed with private keys?
- Signing keys stored in secure vault (not in repo)?
- Public keys securely distributed and rotated?
- Signature verification enforced before use?
- Build provenance tracked and attestable?
- Approved list of vendors or package sources?
- License compliance checks performed?
- Private package repository with scanning?
- Lock files committed to source control?
- Removal process for abandoned dependencies?
- SBOM generation automated in pipeline?
- Vulnerability scanner runs on every commit?
- Artifact signing integrated into release process?
- Build attestations created and verified?
- Scanning results visible to developers?
Self-Check
- Do you know every library and version in your application right now?
- Can you detect when a dependency becomes vulnerable within hours?
- How would you know if a downloaded artifact had been tampered with?
- What would you do if a critical transitive dependency had a vulnerability?
Supply chain security isn't about preventing all risk—it's about visibility and verification. Know what code is in your application, verify it hasn't been modified, and detect vulnerabilities promptly. These three controls address 90% of supply chain attacks.
Next Steps
- Inventory Your Dependencies: Generate SBOMs for all applications using Syft or CycloneDX
- Set Up Scanning: Integrate automated vulnerability scanning in your CI/CD pipeline
- Implement Signing: Start signing release artifacts and container images
- Establish Governance: Define approved package sources and update policies
- Monitor Advisories: Subscribe to vulnerability feeds for your dependencies