From b656617183f11bc696db74f1392a43509b31094a Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Wed, 4 Nov 2026 00:28:23 +0100 Subject: [PATCH] Allow packages from the distribution's own package index unpinned (closes #72) A package from the distribution's own package index now needs no version and no hash, and pulling that index is allowed, on sneak's answer at https://git.eeqj.de/sneak/prompts/issues/72#issuecomment-165196. The distribution is the pinned base image's in a Dockerfile and the host's on a host. It is trusted, and everything else stays pinned by hash. A package source that a Dockerfile adds itself does not get the exception. The pinning rule of REPO_POLICIES.md and both checklists say so, and the canonical Go Dockerfile's apk add line has a comment saying why it is allowed. Model: opus-5-5 --- TODO.md | 9 +++++ prompts/EXISTING_REPO_CHECKLIST.md | 7 +++- prompts/NEW_REPO_CHECKLIST.md | 6 +++- prompts/REPO_POLICIES.md | 54 ++++++++++++++++++++---------- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/TODO.md b/TODO.md index 49e890c..83994d8 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,15 @@ fmt-check, and commit. # Completed Steps +- 2026-11-03: A package from the distribution's own package index now needs no + version and no hash, and pulling that index is allowed + (https://git.eeqj.de/sneak/prompts/issues/72), on sneak's answer at + https://git.eeqj.de/sneak/prompts/issues/72#issuecomment-165196. The + distribution is the pinned base image's in a `Dockerfile` and the host's on a + host. It is trusted, and everything else stays pinned by hash. A package + source that a `Dockerfile` adds itself does not get the exception. The pinning + rule of `REPO_POLICIES.md` and both checklists say so, and the canonical Go + `Dockerfile`'s `apk add` line has a comment saying why it is allowed. - 2026-11-03: A first test run that timed out now gets no verbose rerun (https://git.eeqj.de/sneak/prompts/issues/61), on sneak's approval at https://git.eeqj.de/sneak/prompts/issues/61#issuecomment-165195. The Go recipe diff --git a/prompts/EXISTING_REPO_CHECKLIST.md b/prompts/EXISTING_REPO_CHECKLIST.md index 47a92ed..0c18893 100644 --- a/prompts/EXISTING_REPO_CHECKLIST.md +++ b/prompts/EXISTING_REPO_CHECKLIST.md @@ -216,7 +216,12 @@ with your task. - [ ] Pre-commit hook is installed (`make hooks`) - [ ] No secrets in the repo (`.env`, keys, credentials) - [ ] No mutable references in Dockerfiles or scripts (tags, `@latest`) — all - pinned by cryptographic hash with version/date comment + pinned by cryptographic hash with version/date comment. The one exception + is a package from the distribution's own package index, which needs no + version and no hash. Pulling that index is also allowed. In a + `Dockerfile`, the distribution is the pinned base image's. On a host, it + is the host's. A package source that a `Dockerfile` adds itself does not + get this exception - [ ] Using `yarn`, not `npm` (JS projects) # Directory Structure diff --git a/prompts/NEW_REPO_CHECKLIST.md b/prompts/NEW_REPO_CHECKLIST.md index fd309cb..2e8a6b8 100644 --- a/prompts/NEW_REPO_CHECKLIST.md +++ b/prompts/NEW_REPO_CHECKLIST.md @@ -243,7 +243,11 @@ are thin shims calling them. Model scripts: final stage is missing its `COPY --from=` edge to the gate phases. - [ ] No secrets in repo, and none in the build context: enumerate a probe image rather than reading `.dockerignore` -- [ ] No mutable image/package references +- [ ] No mutable image/package references. The one exception is a package from + the distribution's own package index, which needs no version and no hash. + Pulling that index is also allowed. In a `Dockerfile`, the distribution is + the pinned base image's. On a host, it is the host's. A package source + that a `Dockerfile` adds itself does not get this exception - [ ] No unnecessary files in repo root - [ ] All dates written as YYYY-MM-DD diff --git a/prompts/REPO_POLICIES.md b/prompts/REPO_POLICIES.md index c075882..d6e16a4 100644 --- a/prompts/REPO_POLICIES.md +++ b/prompts/REPO_POLICIES.md @@ -20,19 +20,36 @@ style conventions are in separate documents: `last_modified: YYYY-MM-DD` in the YAML front matter so it can be kept in sync with the authoritative source as policies evolve. -- **ALL external references must be pinned by cryptographic hash.** This - includes Docker base images, Go modules, npm packages, GitHub Actions, and - anything else fetched from a remote source. Version tags (`@v4`, `@latest`, - `:3.21`, etc.) are server-mutable and therefore remote code execution - vulnerabilities. The ONLY acceptable way to reference an external dependency - is by its content hash (Docker `@sha256:...`, Go module hash in `go.sum`, npm - integrity hash in lockfile, GitHub Actions `@`). No exceptions. - This also means never `curl | bash` to install tools like pyenv, nvm, rustup, - etc. Instead, download a specific release archive from GitHub, verify its hash - (hardcoded in the Dockerfile or script), and only then install. Unverified - install scripts are arbitrary remote code execution. This is the single most - important rule in this document. Double-check every external reference in - every file before committing. There are zero exceptions to this rule. +- **External references must be pinned by cryptographic hash.** This includes + Docker base images, Go modules, npm packages, GitHub Actions, and anything + else fetched from a remote source. Version tags (`@v4`, `@latest`, `:3.21`, + etc.) are server-mutable and therefore remote code execution vulnerabilities. + Reference an external dependency by its content hash (Docker `@sha256:...`, Go + module hash in `go.sum`, npm integrity hash in lockfile, GitHub Actions + `@`). This also means never `curl | bash` to install tools like + pyenv, nvm, rustup, etc. Instead, download a specific release archive from + GitHub, verify its hash (hardcoded in the Dockerfile or script), and only then + install. Unverified install scripts are arbitrary remote code execution. This + is the single most important rule in this document. Double-check every + external reference in every file before committing. + + There is one exception: a package from the distribution's own package index + needs no version and no hash. Pulling that index is also allowed. The + distribution is trusted. In a `Dockerfile`, it is the pinned base image's + distribution. On a host, it is the host's. These commands are examples: + - `apt-get update` + - `apt-get install` + - `apk update` + - `apk add` + + These are not trusted, so everything from them stays pinned by hash: + - Docker Hub + - PyPI + - npm + - `go get` + + A package source that a `Dockerfile` adds itself is not the distribution's + own, so the exception does not cover it. - Every repo with software must have a root `Makefile` with these targets: `make bootstrap`, `make setup`, `make test`, `make lint`, `make fmt` (writes), @@ -259,6 +276,8 @@ style conventions are in separate documents: # run unless lint and test passed. # golang:1.x-alpine, YYYY-MM-DD FROM golang@sha256:... AS builder + # git comes from the base image's own distribution package index, which + # is trusted, so it needs no version and no hash. RUN apk add --no-cache git WORKDIR /src COPY go.mod go.sum ./ @@ -394,11 +413,10 @@ style conventions are in separate documents: `Pipfile.lock`. It installs no Python there: the tests use the base image's Python, which the image's hash pins. If it installs anything else there, the step goes after `ARG CACHEBUST`. - - Every external reference must be pinned by hash, so `script/bootstrap` - installs pipenv and its dependencies by hash. It does so with - `pip install --require-hashes -r` and a requirements file in `script/` - that lists each of them with `==` and its `--hash`. pip refuses to install - a dependency the file does not list. + - PyPI is not trusted, so `script/bootstrap` installs pipenv and its + dependencies by hash. It does so with `pip install --require-hashes -r` + and a requirements file in `script/` that lists each of them with `==` and + its `--hash`. pip refuses to install a dependency the file does not list. - It then runs `pipenv install --deploy`, with `--dev` when the tests need the development packages. `--deploy` installs only what `Pipfile.lock` names, checks every package against the lockfile's hashes, and fails when