Allow packages from the distribution's own package index unpinned (closes #72)
check / check (push) Canceled after 0s

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
#72 (comment). 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
This commit was merged in pull request #145.
This commit is contained in:
2026-11-04 00:28:23 +01:00
parent 229ea67434
commit b656617183
4 changed files with 56 additions and 20 deletions
+9
View File
@@ -21,6 +21,15 @@ fmt-check, and commit.
# Completed Steps # 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 - 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), on sneak's approval at
https://git.eeqj.de/sneak/prompts/issues/61#issuecomment-165195. The Go recipe https://git.eeqj.de/sneak/prompts/issues/61#issuecomment-165195. The Go recipe
+6 -1
View File
@@ -216,7 +216,12 @@ with your task.
- [ ] Pre-commit hook is installed (`make hooks`) - [ ] Pre-commit hook is installed (`make hooks`)
- [ ] No secrets in the repo (`.env`, keys, credentials) - [ ] No secrets in the repo (`.env`, keys, credentials)
- [ ] No mutable references in Dockerfiles or scripts (tags, `@latest`) — all - [ ] 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) - [ ] Using `yarn`, not `npm` (JS projects)
# Directory Structure # Directory Structure
+5 -1
View File
@@ -243,7 +243,11 @@ are thin shims calling them. Model scripts:
final stage is missing its `COPY --from=` edge to the gate phases. 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 - [ ] No secrets in repo, and none in the build context: enumerate a probe image
rather than reading `.dockerignore` 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 - [ ] No unnecessary files in repo root
- [ ] All dates written as YYYY-MM-DD - [ ] All dates written as YYYY-MM-DD
+36 -18
View File
@@ -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 `last_modified: YYYY-MM-DD` in the YAML front matter so it can be kept in sync
with the authoritative source as policies evolve. with the authoritative source as policies evolve.
- **ALL external references must be pinned by cryptographic hash.** This - **External references must be pinned by cryptographic hash.** This includes
includes Docker base images, Go modules, npm packages, GitHub Actions, and Docker base images, Go modules, npm packages, GitHub Actions, and anything
anything else fetched from a remote source. Version tags (`@v4`, `@latest`, else fetched from a remote source. Version tags (`@v4`, `@latest`, `:3.21`,
`:3.21`, etc.) are server-mutable and therefore remote code execution etc.) are server-mutable and therefore remote code execution vulnerabilities.
vulnerabilities. The ONLY acceptable way to reference an external dependency Reference an external dependency by its content hash (Docker `@sha256:...`, Go
is by its content hash (Docker `@sha256:...`, Go module hash in `go.sum`, npm module hash in `go.sum`, npm integrity hash in lockfile, GitHub Actions
integrity hash in lockfile, GitHub Actions `@<commit-sha>`). No exceptions. `@<commit-sha>`). This also means never `curl | bash` to install tools like
This also means never `curl | bash` to install tools like pyenv, nvm, rustup, pyenv, nvm, rustup, etc. Instead, download a specific release archive from
etc. Instead, download a specific release archive from GitHub, verify its hash GitHub, verify its hash (hardcoded in the Dockerfile or script), and only then
(hardcoded in the Dockerfile or script), and only then install. Unverified install. Unverified install scripts are arbitrary remote code execution. This
install scripts are arbitrary remote code execution. This is the single most is the single most important rule in this document. Double-check every
important rule in this document. Double-check every external reference in external reference in every file before committing.
every file before committing. There are zero exceptions to this rule.
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: - Every repo with software must have a root `Makefile` with these targets:
`make bootstrap`, `make setup`, `make test`, `make lint`, `make fmt` (writes), `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. # run unless lint and test passed.
# golang:1.x-alpine, YYYY-MM-DD # golang:1.x-alpine, YYYY-MM-DD
FROM golang@sha256:... AS builder 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 RUN apk add --no-cache git
WORKDIR /src WORKDIR /src
COPY go.mod go.sum ./ 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 `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 image's Python, which the image's hash pins. If it installs anything else
there, the step goes after `ARG CACHEBUST`. there, the step goes after `ARG CACHEBUST`.
- Every external reference must be pinned by hash, so `script/bootstrap` - PyPI is not trusted, so `script/bootstrap` installs pipenv and its
installs pipenv and its dependencies by hash. It does so with dependencies by hash. It does so with `pip install --require-hashes -r`
`pip install --require-hashes -r` and a requirements file in `script/` and a requirements file in `script/` that lists each of them with `==` and
that lists each of them with `==` and its `--hash`. pip refuses to install its `--hash`. pip refuses to install a dependency the file does not list.
a dependency the file does not list.
- It then runs `pipenv install --deploy`, with `--dev` when the tests need - It then runs `pipenv install --deploy`, with `--dev` when the tests need
the development packages. `--deploy` installs only what `Pipfile.lock` the development packages. `--deploy` installs only what `Pipfile.lock`
names, checks every package against the lockfile's hashes, and fails when names, checks every package against the lockfile's hashes, and fails when