Supply Chain Security Practices¶
Knowing the threats isn't enough. This chapter covers the practical habits that reduce risk over time—dependency updates, artifact signing, and the security practices that become second nature.
The Boring Part
Updates are the most boring security practice. That's why they matter most. Nobody gets excited about bumping patch versions. But the teams I've seen handle incidents well are the ones who do the boring work consistently. Security isn't a heroic act—it's a thousand small habits.
Dependency Update Strategies¶
Updates are a double-edged sword: stale dependencies accumulate vulnerabilities, but updates can break things. You need a strategy.
Update Cadence¶
| Update Type | Frequency | Approach |
|---|---|---|
| Security patches | Immediately | Prioritize over everything |
| Patch versions | Weekly/bi-weekly | Usually safe, test and merge |
| Minor versions | Monthly | Review changelog, test thoroughly |
| Major versions | Planned | Schedule time, expect breaking changes |
Automated Update PRs¶
Dependabot (GitHub) or Renovate (self-hostable) automate the tedious parts:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
development-dependencies:
dependency-type: "development"
production-dependencies:
dependency-type: "production"
Renovate offers more control:
{
"extends": ["config:base"],
"schedule": ["before 6am on monday"],
"automerge": true,
"automergeType": "branch",
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"automerge": true
},
{
"matchUpdateTypes": ["major"],
"automerge": false
}
]
}
The Update Philosophy¶
Updates aren't free—they cost testing time. But stale dependencies are debt—compounding.
Find a sustainable rhythm:
- Security updates: Always. No exceptions.
- Everything else: Regular cadence that your team can sustain
- Major updates: Planned, not reactive
Never disable automated security updates to reduce noise. The noise is telling you something.
Lock Files¶
Lock files record exact resolved versions of all dependencies. They're not optional.
What Lock Files Do¶
- Capture exact versions —
lodash@4.17.21, notlodash@^4.17.0 - Include transitive dependencies — Everything, not just direct
- Record integrity hashes — Verify downloads haven't been tampered with
- Enable reproducible builds — Same input → same output
Lock Files by Ecosystem¶
| Ecosystem | Lock File | Command to Generate |
|---|---|---|
| npm | package-lock.json | npm install |
| Yarn | yarn.lock | yarn |
| pnpm | pnpm-lock.yaml | pnpm install |
| pip | requirements.txt (weak) | pip freeze |
| pip-tools | requirements.txt | pip-compile |
| Poetry | poetry.lock | poetry lock |
| uv | uv.lock | uv lock |
| Bundler | Gemfile.lock | bundle install |
| Cargo | Cargo.lock | cargo build |
| Go | go.sum | go mod tidy |
Lock File Rules¶
1. Lock files go in version control.
# YES
git add package-lock.json
git commit -m "Update dependencies"
# NO
echo "package-lock.json" >> .gitignore # Don't do this
2. CI installs from lock file.
# npm: use ci, not install
npm ci # Installs exactly what's in lock file
# pip: don't resolve, use frozen requirements
pip install -r requirements.txt --no-deps
3. Update lock files intentionally.
Don't let CI regenerate lock files. Updates should be explicit:
4. Review lock file changes in PRs.
When a PR includes lock file changes, review them:
- What versions changed?
- Were new dependencies added?
- Did any dependencies disappear?
Artifact Signing¶
Code signing verifies authenticity: this artifact came from who it claims to come from, and hasn't been modified.
Why Sign?¶
- Authenticity — Verify the claimed source
- Integrity — Detect tampering
- Non-repudiation — Prove who published what
- Chain of custody — Traceable provenance
Signing Mechanisms¶
Traditional (GPG):
# Sign a file
gpg --armor --detach-sign artifact.tar.gz
# Verify
gpg --verify artifact.tar.gz.asc artifact.tar.gz
GPG works but has usability issues—key management is painful.
Modern (Sigstore/cosign):
# Sign a container image (keyless)
cosign sign myregistry/myimage:v1.0
# Verify
cosign verify myregistry/myimage:v1.0
Sigstore uses OpenID Connect for identity—no key management required. Sign with your GitHub/Google/Microsoft identity.
npm Provenance¶
npm supports provenance attestations for packages published from CI:
# GitHub Actions
- name: Publish with provenance
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Consumers can verify packages were built from claimed source code.
Verification in CI¶
# Verify signatures before deployment
- name: Verify image signature
run: |
cosign verify \
--certificate-identity=ci@myorg.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
myregistry/myimage:${{ github.sha }}
SLSA Framework¶
SLSA (Supply-chain Levels for Software Artifacts, pronounced "salsa") is a framework for supply chain integrity.1
SLSA Levels¶
| Level | Requirements | What It Means |
|---|---|---|
| 1 | Documented build process | You can explain how artifacts are built |
| 2 | Hosted build, signed provenance | Builds run on service infrastructure, provenance is tamper-evident |
| 3 | Hardened build, non-falsifiable provenance | Build service is hardened, provenance can't be faked |
| 4 | Two-party review, hermetic builds | All changes reviewed, builds are fully reproducible |
Practical SLSA¶
Most projects should target Level 2-3:
- Use a CI service (GitHub Actions, GitLab CI)
- Generate provenance attestations
- Sign artifacts
Level 4 is for high-security contexts—it requires hermetic builds (no network access during build) and two-party review for all changes.
GitHub Actions SLSA Generator:
- uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.0.0
with:
artifact-name: my-artifact
This generates SLSA Level 3 provenance for your artifacts.
Repository Security¶
For Package Maintainers¶
If you maintain packages others depend on:
Enable 2FA. Required on npm and PyPI for popular packages. Do it anyway.
Use fine-grained tokens. Don't use personal tokens for CI. Create scoped tokens with minimal permissions.
Require review for releases. Tag protection, branch protection, and release approval workflows.
Sign your releases. Use Sigstore, GPG, or your registry's provenance features.
Publish a SECURITY.md. Tell people how to report vulnerabilities.
# Security Policy
## Reporting a Vulnerability
Please report security vulnerabilities to security@example.com.
Do not open public issues for security problems.
We aim to respond within 48 hours and patch within 7 days.
For Package Consumers¶
Use lock files. Already covered—don't skip this.
Verify checksums. Lock files include hashes. CI should verify them.
Consider private registries. For organizations, mirror approved packages:
- Artifactory
- Nexus
- Verdaccio (npm)
- devpi (Python)
Private registries give you:
- Caching (faster builds)
- Availability (builds don't break if upstream is down)
- Control (approve packages before use)
Monitor for anomalies. Watch for:
- New maintainers on critical dependencies
- Unusual release patterns
- Suspicious new dependencies
Defense Checklist¶
Minimum Viable Security¶
- Lock files in version control
- CI installs from lock file
- Automated vulnerability scanning
- 2FA on package registry accounts
- Secrets not in source code
Better Security¶
- Automated dependency update PRs
- SBOM generation on every build
- Lock file changes reviewed in PRs
- Private registry mirror
- Signed artifacts
Advanced Security¶
- SLSA Level 2+ compliance
- Hermetic builds
- Provenance verification in deployment
- Anomaly detection on dependencies
- Regular security audits
Habits, Not Projects
Security practices are habits, not projects. You don't "do security" once—you build it into how you work.
The teams I've seen handle incidents well have these habits deeply embedded. Lock files are always committed. Vulnerability scans run on every PR. Updates happen on a predictable cadence. When something goes wrong, they're responding from a position of knowledge, not scrambling to figure out what they're even running.
The teams that struggle treat security as an obstacle. They disable scanning because it's noisy. They skip lock files because they're confusing. They defer updates because they might break things.
Both teams eventually face the same incidents. One is prepared. One isn't. Build the habits now, before you need them.
-
See SLSA Framework ↩