Supply Chain Security Patterns
Dependency Pinning
# requirements.txt — pin exact versions + hashes
pip-compile --generate-hashes requirements.in > requirements.txt
# Result:
# cryptography==42.0.5 \
# --hash=sha256:6d0fbe82a65d3... \
# --hash=sha256:9a8d6802e0825...
# Install with hash verification
pip install --require-hashes -r requirements.txt
// package.json — use exact versions, lockfile
{
"dependencies": {
"express": "4.18.2" // Exact, not ^4.18.2
}
}
// .npmrc — enforce lockfile
save-exact=true
package-lock-only=false
# Cargo.lock committed — pin all transitive deps
# Cargo.toml
[dependencies]
tokio = { version = "=1.36.0", features = ["full"] } # Exact version pin
SBOM Generation
# Syft — generate SBOM from image or directory
brew install syft
# CycloneDX format (preferred)
syft myapp:latest -o cyclonedx-json > sbom.cyclonedx.json
# SPDX format
syft myapp:latest -o spdx-json > sbom.spdx.json
# Scan SBOM for vulnerabilities with Grype
grype sbom:sbom.cyclonedx.json
# Python — pip-licenses
pip install pip-licenses
pip-licenses --format=json --with-urls > licenses.json
# Node — cyclonedx
npm install -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --output-file sbom.json
Image Signing with Cosign
# Install cosign
brew install cosign
# Generate key pair
cosign generate-key-pair
# Sign image after push
cosign sign --key cosign.key myregistry.io/myapp:v1.0.0
# Verify before deploy
cosign verify --key cosign.pub myregistry.io/myapp:v1.0.0
# Keyless signing (uses OIDC — preferred in CI)
cosign sign myregistry.io/myapp:v1.0.0 # Uses GitHub/Google OIDC
# Verify keyless
cosign verify \
--certificate-identity-regexp "https://github.com/myorg/myrepo" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
myregistry.io/myapp:v1.0.0
Provenance Attestation (SLSA)
# GitHub Actions — generate SLSA provenance
name: Build and Attest
on: [push]
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
attestations: write
steps:
- uses: actions/checkout@v4
- name: Build image
run: |
docker build -t myapp:${{ github.sha }} .
docker push myregistry.io/myapp:${{ github.sha }}
- name: Attest build provenance
uses: actions/attest-build-provenance@v1
with:
subject-name: myregistry.io/myapp
subject-digest: sha256:${{ steps.push.outputs.digest }}
push-to-registry: true
# Verify attestation
- name: Verify provenance
run: |
gh attestation verify oci://myregistry.io/myapp:${{ github.sha }} \
--owner myorg
SLSA Levels
SLSA Level 1 — Documented build process:
✓ Build script exists
✓ SBOM generated
✓ Provenance available (unsigned)
SLSA Level 2 — Build service:
✓ Version controlled source
✓ Hosted build service (GitHub Actions, Cloud Build)
✓ Signed provenance
SLSA Level 3 — Hardened builds:
✓ Source integrity (two-party review)
✓ Hermetic/isolated build environment
✓ Non-falsifiable provenance
✓ Reproducible builds
SLSA Level 4 — Two-person reviewed:
✓ 4-eyes principle on all changes
✓ Hermetic, reproducible builds verified
Admission Control (Kubernetes)
# Kyverno policy — require signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
resources:
kinds: [Pod]
verifyImages:
- imageReferences: ["myregistry.io/*"]
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
Checklist
Dependencies:
✓ All deps pinned to exact versions with hashes
✓ Lockfiles committed (package-lock.json, Cargo.lock, poetry.lock)
✓ Dependency updates automated via Dependabot/Renovate
✓ Vulnerability alerts enabled on repo
Build:
✓ Builds run in ephemeral, isolated environments
✓ Build inputs (source + deps) fully declared
✓ SBOM generated and attached to release artifacts
✓ Images signed with Cosign
Distribution:
✓ Provenance attestation attached to artifacts
✓ Admission controller verifies signatures before deploy
✓ Private registry with access control (no public pull)
✓ Artifact retention policy defined