dependency-pinning
Skill6 starsChanged 4 months agoMITView on GitHub
Audit Docker, CI, and language dependencies for SHA/digest pinning and cooldowns.
- Installs packages
In this repository27 other files it gives its agents.
- agent-skills-agentsAGENTS.md
- agent-skills-claudeCLAUDE.md
- agent-log-forensicsSkill
- changelog-writerSkill
- chezmoi-driftSkill
- contacts-unifySkill
- decision-logSkill
- django-smoke-alarmSkill
- empathy-auditSkill
- first-contactSkill
- handle-prSkill
- launch-sequenceSkill
- parallel-isolated-app-testingSkill
- post-mortemSkill
- pre-mortemSkill
- profile-meSkill
- ralph-loopSkill
- release-operatorSkill
- remote-host-verifierSkill
- research-personSkill
- scope-hammerSkill
- second-opinionsSkill
- status-copy-trust-auditSkill
- support-stormSkill
- trust-auditSkill
- visual-qa-loopSkill
- wide-open-brainstormSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 8 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
Ask what to scan if unclear: a single project, a list of repos, or "everything under a directory the user names".
- Distinctive: line 65
Cooldown: no native support — enforce by choosing a digest for an old-enough tag.
- Very specific: line 79
Bad: no lockfile committed; relying on ^/~/latest without a lock.
- Very specific: line 108
Cooldown: no native NuGet cooldown — use Renovate minimumReleaseAge (NuGet supported).
- Very specific: line 112
Rust/Cargo: commit Cargo.lock (carries crates.io sha256 checksums).
- Very specific: line 113
No native cooldown → Renovate minimumReleaseAge (cargo datasource).
- Very specific: line 120
Ruby/Bundler: commit Gemfile.lock; bundle config set frozen true.
- Distinctive: line 126
Don't stop at the languages listed here.
- Distinctive: line 170
Default to read-only. Do not edit files, run installers, or commit until the user approves.
- Distinctive: line 171
When approved, change one ecosystem at a time.
- Distinctive: line 172
Every pin bump names the new SHA/digest and reminds the user to do a supply-chain check and respect the cooldown (no artifact newer than N …
- Very specific: line 174
Never weaken an existing pin.
- Distinctive: line 175
Keep the human-readable version in a trailing comment beside every SHA pin.
- Distinctive: line 176
Commit lockfiles — never gitignore them.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dependency Pinning
A dependency safety scan — not a CVE scan. It checks two things across every ecosystem in scope:
- Pinning — dependencies pinned to an immutable content SHA / digest, not a movable tag. Tags can be silently re-pointed to altered or malicious artifacts (the tj-actions class of supply-chain attacks). A digest can't.
- Cooldown — a project never adopts an artifact newer than N days (default 7). A fresh release is where a compromise lands first; the cooldown buys time for it to be caught before it reaches the build.
Report first. Mutate only on explicit approval. Bumping a pin is itself risky, so every proposed change names the new SHA and flags it for a supply-chain check.
Scope
Ask what to scan if unclear: a single project, a list of repos, or "everything under
a directory the user names". Don't scan outside what the user named. Then discover dependency manifests (this prunes bin/obj/packages directories, so say if a .NET or other repo keeps manifests there):
ROOT="${1:-.}"
/usr/bin/find "$ROOT" -type d \( -name node_modules -o -name .git -o -name .terraform \
-o -name vendor -o -name packages -o -name bin -o -name obj -o -path '*/assets/libs' \) -prune -o \
-type f \( \
-iname 'Dockerfile*' -o -name 'docker-compose*.y*ml' -o -name 'compose*.y*ml' \
-o -path '*/.github/workflows/*.y*ml' \
-o -name 'package.json' -o -name 'deno.json*' \
-o -name 'pyproject.toml' -o -name 'requirements*.txt' \
-o -name '*.csproj' -o -name 'packages.config' -o -name 'Directory.Packages.props' \
-o -name 'Cargo.toml' -o -name 'go.mod' \
-o -name 'pom.xml' -o -name 'build.gradle' -o -name 'build.gradle.kts' \
-o -name 'Gemfile' -o -name '*.gemspec' \
-o -name '*.tf' \
\) -print 2>/dev/null
# Also catch images pulled from scripts/CI, not just Dockerfiles/compose:
grep -rEn 'docker (run|pull|build)[^|]*[a-z0-9./-]+:[a-z0-9._-]+' "$ROOT" \
--include='*.sh' --include='*.y*ml' --include='Makefile' 2>/dev/nullIgnore vendored/minified asset trees (*/assets/**, *.min.*) — those package.json
files are bundled libraries, not your declared dependencies.
What "good" looks like, how to detect violations, how to fix
Docker / Compose (pinning: required · cooldown: manual)
- Good:
FROM repo/img@sha256:<digest>·image: repo/img@sha256:<digest> - Bad:
:latest, floating tags, no digest. - Detect:
grep -rEn '^\s*FROM |image:\s' <files>→ flag any ref without@sha256:. Also scan scripts/CI/Makefiles fordocker run|pull|build ... img:tag— images pulled outside Dockerfiles/compose are easy to miss and just as swappable. - Fix: resolve the digest of a tag that is ≥ N days old:
docker buildx imagetools inspect repo/img:<ver> --format '{{.Manifest.Digest}}'then writerepo/img@sha256:<digest> # <ver>. - Cooldown: no native support — enforce by choosing a digest for an old-enough tag.
GitHub Actions (pinning: required · cooldown: via tooling)
- Good:
uses: owner/repo@<40-hex-sha> # vX.Y.Z - Bad:
@v4,@main,@<branch>. - Detect:
grep -rEn 'uses:\s' .github/workflows→ flag any ref not matching a 40-char hex SHA. - Fix:
git ls-remote https://github.com/<owner>/<repo> refs/tags/<ver>→ SHA; pin with the version in a trailing comment. - Cooldown / automation:
ratchet(sethvargo/ratchet) orpinactpin+update actions by SHA; Renovate/DependabotminimumReleaseAge/ cooldown gates the version that gets pinned.
JavaScript — npm / yarn / pnpm / bun / deno (pinning: lockfile · cooldown: tooling)
- Good: lockfile committed (
package-lock.json/yarn.lock/pnpm-lock.yaml/bun.lock/deno.lock) with integrity hashes, and CI installs frozen (npm ci,pnpm i --frozen-lockfile,yarn --immutable,deno install --frozen). - Bad: no lockfile committed; relying on
^/~/latestwithout a lock;npm installin CI. - Detect: manifest present but lockfile missing/gitignored; CI using non-frozen installs.
- Fix: generate + commit the lockfile; switch CI to the frozen install command.
- Cooldown: pnpm has native
minimumReleaseAge(config/.npmrc). For npm/yarn/bun, add RenovateminimumReleaseAge: "7 days"(or Dependabot cooldown). Deno: pin exact versions in import map /deno.json;deno.lockcarries integrity.
Python — uv / pdm / poetry / pip (pinning: lockfile+hashes · cooldown: uv native / tooling)
- Good: lockfile committed with hashes (
uv.lock,pdm.lock,poetry.lock) and installed locked (uv sync --locked,pdm sync,poetry install); for bare pip, hashed pins viapip-compile --generate-hashes+pip install --require-hashes. - Bad: unpinned/
>=/*requirements, no lockfile, no hashes. - Detect:
pyproject.toml/requirements.txtpresent but no lockfile/hashes; ranges without a lock. - Fix: adopt a lock workflow or generate hashed requirements; commit the lock.
- Cooldown: uv has native
exclude-newer/UV_EXCLUDE_NEWER(resolve as of a date — a true cooldown). Otherwise RenovateminimumReleaseAge.
.NET — NuGet (pinning: exact versions + lockfile · cooldown: tooling)
- Good: exact versions (
packages.configversion="x.y.z", or<PackageReference Version="x.y.z"/>with no range/*) plus a committedpackages.lock.json(enable<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>, restore withdotnet restore --locked-mode). The lockfile carriescontentHashper package. - Bad: floating versions (
Version="*",Version="[1.0,)"), no lockfile, packages restored from a non-pinned feed. - Detect:
grep -rEn 'Version="\*"|Version="\[' **/*.csproj;.csproj/packages.configpresent but nopackages.lock.jsonbeside it; checkDirectory.Packages.propsfor central versions if used. - Fix: pin exact versions; enable + commit
packages.lock.json; pin the feed innuget.config. - Cooldown: no native NuGet cooldown — use Renovate
minimumReleaseAge(NuGet supported).
Rust — Cargo · Go · Java (Maven/Gradle) · Ruby (Bundler)
Terser, but the same two questions apply (immutable pin? cooldown?):
- Rust/Cargo: commit
Cargo.lock(carries crates.io sha256 checksums);cargo --locked. No native cooldown → RenovateminimumReleaseAge(cargo datasource).cargo audit/cargo vetadjacent. - Go modules:
go.mod+go.sum(cryptographic hashes, verified vs the checksum DB);go mod verify, build with-mod=readonly. No native cooldown → Renovate (gomod). - Java/Maven: pin exact versions in
pom.xml— banLATEST/RELEASE/version ranges (enforce withmaven-enforcerrequireReleaseDeps / ban-dynamic-versions). No lockfile natively. - Java/Gradle: enable dependency locking (
gradle.lockfile) + verification metadata (gradle --write-verification-metadata sha256→verification-metadata.xmlwith checksums/signatures). - Ruby/Bundler: commit
Gemfile.lock;bundle config set frozen true; add checksums withbundle lock --add-checksums(Bundler 2.5+). No native cooldown → Renovate (bundler). - Cooldown across all of the above is best delivered by Renovate
minimumReleaseAge/ Dependabot cooldown, which support these datasources.
Any other ecosystem you encounter (general rule)
Don't stop at the languages listed here. For any package manager in the target:
- Identify the manifest + its lockfile; confirm the lockfile carries integrity hashes and is committed, and that CI installs frozen/locked.
- Flag floating/range/
latestversion specifiers in the manifest. - Research whether that manager supports a cooldown — native settings first
(names vary:
minimumReleaseAge,exclude-newer,--as-of, min-age, quarantine), then a meta-tool (RenovateminimumReleaseAge/ Dependabot cooldown) for that datasource. If unsure, check the tool's current docs rather than guessing. - Report findings the same way and propose the concrete setting to add.
Terraform (pinning: lock + version constraints)
- Good:
.terraform.lock.hclcommitted with multi-platform hashes (terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 ...); providers pinned inrequired_providers; modules pinned by version or git commit SHA; AMIs/data sources pinned by ID (nomost_recent = true). - Bad: lock gitignored, missing platforms,
most_recent = true, modules onmain. - Detect: lock in
.gitignore;grep -rn 'most_recent\s*=\s*true' *.tf; module sources on a branch. - Fix: un-ignore + commit the lock (multi-platform); pin module/AMI refs.
Adding cooldowns where supported (offer these)
- Renovate (cross-ecosystem):
"minimumReleaseAge": "7 days"inrenovate.json— the single best lever; gates npm/pip/docker/actions/etc. behind the cooldown. - Dependabot:
cooldown:block in.github/dependabot.yml. - pnpm:
minimumReleaseAge· uv:exclude-newer· GH Actions: ratchet/pinact.
Report format
DEPENDENCY SAFETY REPORT (cooldown target: 7 days)
===================================================
<repo/path>
Docker : 2 images unpinned (FROM node:20, postgres:16) — PIN MISSING
GH Actions : 3 actions on tags (@v4) — PIN MISSING
JS (pnpm) : lockfile committed ✓ · no cooldown — COOLDOWN MISSING
Python (uv) : uv.lock ✓ · exclude-newer not set — COOLDOWN MISSING
Terraform : .terraform.lock.hcl gitignored — LOCK NOT COMMITTED
...
SUMMARY: 4 pin gaps, 3 cooldown gaps, 1 uncommitted lock across N repos.Classify each as: OK · PIN MISSING · COOLDOWN MISSING · LOCK NOT COMMITTED.
Then list exact fixes (the resolved SHA/digest, the config snippet) per item, ordered by risk: unpinned CI actions and images first, cooldown settings last. Stop after the report and ask which ecosystems to change.
Action rules
- Default to read-only. Do not edit files, run installers, or commit until the user approves.
- When approved, change one ecosystem at a time; show the diff.
- Every pin bump names the new SHA/digest and reminds the user to do a supply-chain check and respect the cooldown (no artifact newer than N days).
- Never weaken an existing pin. Never add a
:latest/floating ref as a "fix". - Keep the human-readable version in a trailing comment beside every SHA pin.
- Commit lockfiles — never gitignore them.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

