Package Management
Dependency management, lockfiles, registries, and supply chain security.
Core Principles
- Lockfiles committed — Every repo commits lockfiles (
Cargo.lock,go.sum,pnpm-lock.yaml,package-lock.json,poetry.lock,gradle.lockfile) - Exact versions — No floating versions (
^,~,*,latest) in lockfiles - Private registry/proxy — Mirror all public registries; block typosquatting
- SBOM generation — Every build produces SPDX/CycloneDX SBOM
- Vulnerability scanning — Automated in CI on every PR
- Signed artifacts — Cosign/Sigstore for container images and binaries
Language-Specific Package Managers
Rust: Cargo
# Cargo.toml
[package]
name = "my-service"
version = "0.1.0"
edition = "2021"
[workspace]
resolver = "2"
members = ["domain", "application", "infrastructure", "api"]
[dependencies]
# Exact versions in lockfile only; semver ranges here OK
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.38", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid"] }
thiserror = "1.0"
anyhow = "1.0"
[dev-dependencies]
mockall = "0.12"
proptest = "1.0"
Commands: cargo update, cargo audit, cargo deny check, cargo tree -d
Go: Go Modules
// go.mod
module github.com/org/my-service
go 1.22
require (
github.com/gin-gonic/gin v1.9.1
github.com/jackc/pgx/v5 v5.5.0
github.com/redis/go-redis/v9 v9.3.0
go.uber.org/zap v1.26.0
golang.org/x/sync v0.5.0
)
require (
// indirect dependencies - managed by go mod tidy
)
# go.sum committed
go mod tidy
go mod verify
go mod download
govulncheck ./...
Private Modules
# ~/.gitconfig or git config
[url "ssh://git@github.com/"]
insteadOf = https://github.com/
# GOPRIVATE for private modules
export GOPRIVATE=github.com/myorg/*
Python: uv (Recommended) / Poetry / PDM
# pyproject.toml (uv)
[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi==0.109.0",
"pydantic==2.6.0",
"pydantic-settings==2.2.0",
"sqlalchemy==2.0.25",
"asyncpg==0.29.0",
"redis==5.0.1",
"pydantic[email]==2.6.0",
]
[project.optional-dependencies]
dev = [
"pytest==8.0.0",
"pytest-asyncio==0.23.0",
"pytest-cov==4.1.0",
"ruff==0.3.0",
"mypy==1.8.0",
]
[tool.uv]
dev-dependencies = ["dev"]
# uv.lock committed
uv sync
uv lock
uv pip compile --generate-hashes pyproject.toml -o requirements.txt
pip-audit -r requirements.txt
JavaScript/TypeScript: pnpm (Recommended)
// package.json
{
"name": "my-frontend",
"version": "1.0.0",
"private": true,
"packageManager": "pnpm@9.0.0",
"engines": { "node": ">=20" },
"dependencies": {
"react": "18.3.0",
"react-dom": "18.3.0",
"react-router-dom": "6.24.0",
"zod": "3.23.0",
"@tanstack/react-query": "5.28.0"
},
"devDependencies": {
"@types/react": "18.3.0",
"@types/react-dom": "18.3.0",
"typescript": "5.4.0",
"vite": "5.3.0",
"biome": "1.8.0"
}
}
# pnpm-lock.yaml committed
pnpm install --frozen-lockfile
pnpm audit
pnpm update --interactive
Monorepo (pnpm Workspace)
# pnpm-workspace.yaml
packages:
- packages/*
- services/*
- tools/*
// package.json (root)
{
"private": true,
"workspaces": ["packages/*", "services/*", "tools/*"],
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint"
}
}
JVM: Gradle (Kotlin DSL) — Recommended
// build.gradle.kts (root)
plugins {
id("org.springframework.boot") version "3.2.0" apply false
id("io.spring.dependency-management") version "1.1.4" apply false
kotlin("jvm") version "1.9.0" apply false
kotlin("plugin.spring") version "1.9.0" apply false
kotlin("plugin.serialization") version "1.9.0" apply false
}
dependencyResolutionManagement {
repositories {
mavenCentral()
maven { url = uri("https://maven.pkg.github.com/myorg/*") }
gradlePluginPortal()
}
// Resolution strategy for consistent versions
resolutionStrategy {
eachDependency { details ->
if (details.requested.group == "org.springframework") {
details.useVersion("6.1.0")
}
}
}
}
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
resolutionStrategy {
eachPlugin { details ->
if (details.id.id == "org.springframework.boot") {
details.useVersion("3.2.0")
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://maven.pkg.github.com/myorg") }
}
}
rootProject.name = "my-platform"
include("domain", "application", "infrastructure", "api")
// api/build.gradle.kts
plugins {
id("org.springframework.boot")
id("io.spring.dependency-management")
kotlin("jvm")
kotlin("plugin.spring")
kotlin("plugin.serialization")
}
dependencies {
implementation(project(":domain"))
implementation(project(":application"))
implementation(project(":infrastructure"))
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
}
# gradle.lockfile committed (Gradle 7.5+)
./gradlew dependencies --write-locks
./gradlew dependencyUpdates
Private Registry / Proxy
Why Mirror Public Registries?
- Typosquatting protection —
lodashvslodashhblocked - Availability — Public registry outage doesn't break your builds
- Speed — Local cache = faster CI
- Control — Scan on ingest; approve/deny packages
- License compliance — Enforce allowed licenses
Registry Options
| Type | Tool | Best For |
|---|---|---|
| NPM | Verdaccio, Nexus, Artifactory, GitHub Packages | JS/TS |
| PyPI | devpi, Nexus, Artifactory, GitHub Packages | Python |
| Maven | Nexus, Artifactory, GitHub Packages | JVM |
| Cargo | crates.io mirror, private registry (cargo-registries) | Rust |
| Go | Athens, GOPROXY, Go Module Mirror | Go |
| Container | Harbor, Nexus, Artifactory, GHCR, ECR | All (Docker images) |
| Universal | Nexus, Artifactory, JFrog, Cloudsmith | Multi-language |
Example: Verdaccio (NPM Proxy)
# verdaccio/config.yaml
storage: ./storage
auth:
htpasswd:
file: ./htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true
timeout: 100s
packages:
'@myorg/*':
access: $all
publish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
proxy: npmjs
# Client config
npm config set registry http://localhost:4873
npm config set @myorg:registry http://localhost:4873
# Or .npmrc in project
registry=http://localhost:4873
@myorg:registry=http://localhost:4873
SBOM (Software Bill of Materials)
Generate at Build
# Rust
cargo install cargo-cyclonedx
cargo cyclonedx --format json --output sbom.json
# Go
go install github.com/CycloneDX/cyclonedx-go/cmd/cyclonedx-go@latest
cyclonedx-go -mod -json -output sbom.json
# Python
pip install cyclonedx-bom
cyclonedx-py -i requirements.txt -o sbom.json --format json
# JS/TS (pnpm)
pnpm install -g @cyclonedx/bom
cyclonedx-bom -o sbom.json
# JVM (Gradle)
./gradlew cyclonedxBom
# Container (Syft - universal)
syft packages dir:. -o cyclonedx-json=sbom.json
syft packages myorg/my-service:1.0.0 -o cyclonedx-json=sbom.json
SBOM in CI
# .github/workflows/sbom.yml
name: Generate SBOM
on: [push, release]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate SBOM (Syft)
uses: anchore/sbom-action@v0
with:
format: cyclonedx-json
output-file: sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
- name: Scan for Vulns
uses: anchore/scan-action@v3
with:
sbom: sbom.json
severity-cutoff: high
Vulnerability Scanning
In CI Pipeline
# Unified scanning with Trivy (works for all)
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
exit-code: '1'
ignore-unfixed: true
- name: Upload to Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
Language-Specific
# Rust
cargo audit
cargo deny check
# Go
govulncheck ./...
# Python
pip-audit -r requirements.txt
# JS/TS
pnpm audit --prod
npm audit --audit-level=high
# JVM
./gradlew dependencyCheckAnalyze
# or OWASP Dependency Check CLI
Dependency Update Automation
Renovate (Recommended — Most Flexible)
// renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":automergePatch",
":automergeMinor",
":disableUnstable",
":prConcurrentLimit10",
":scheduleWeekly"
],
"packageRules": [
{
"matchFileNames": ["**/Cargo.toml"],
"groupName": "Rust dependencies",
"automerge": false
},
{
"matchFileNames": ["**/go.mod"],
"groupName": "Go dependencies",
"automerge": true
},
{
"matchFileNames": ["**/pyproject.toml"],
"groupName": "Python dependencies",
"automerge": true
},
{
"matchDatasource": "docker",
"enabled": false
}
],
"vulnerabilityAlerts": {
"enabled": true,
"labels": ["security"],
"automerge": true
}
}
Dependabot (GitHub Native)
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
groups:
rust-deps:
patterns: ["*"]
update-types: ["minor", "patch"]
labels: ["dependencies", "rust"]
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
groups:
go-deps:
patterns: ["*"]
labels: ["dependencies", "go"]
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
labels: ["dependencies", "python"]
- package-ecosystem: "npm"
directory: "/frontend"
schedule:
interval: "weekly"
labels: ["dependencies", "npm"]
Supply Chain Security Checklist
- [ ] Lockfiles committed for all languages
- [ ] Private registry/proxy configured for all ecosystems
- [ ] SBOM generated at every build (CycloneDX/SPDX)
- [ ] SBOM signed (Cosign) and uploaded as artifact
- [ ] Vulnerability scanning in CI (Trivy, language-specific)
- [ ] Dependency update automation (Renovate/Dependabot)
- [ ] License scanning (allowed/denied lists)
- [ ] Container images signed (Cosign) and verified on deploy
- [ ] Admission control requires signatures + scan pass
- [ ] Secrets detection in pre-commit + CI
- [ ] SLSA provenance generated (SLSA Level 1+)
Related Notes
- Static Analysis — Dependency scanning in CI
- Build Tools — Build-time dependency resolution
- Supply Chain Security — SLSA, signing, admission
- Secure Coding — Dependency hygiene
- CI CD Best Practices — Pipeline integration
-
- — Language-specific package management
Sources: Cargo, Go Modules, uv, pnpm, Gradle, Maven, npm, PyPI, crates.io, GitHub Packages, Verdaccio, Nexus, Artifactory, CycloneDX, SPDX, Syft, Trivy, Renovate, Dependabot, Cosign, Sigstore, SLSA, OWASP Dependency Check