Skip to content

sec: hash-pin the pip/npm/download-then-run steps in the three Dockerfiles (Scorecard Pinned-Dependencies) #203

Description

@cdeust

Split out of the Pinned-Dependencies work in chore/pin-container-digests, which pinned the five container base images by digest and added Dependabot to refresh them. The remaining warnings in the same Scorecard check are all package-manager invocations inside the Dockerfiles, and they need a different mechanism.

Scorecard (2026-07-27, score 7.2, Pinned-Dependencies 3/10) reports these after the digest pins land:

Warn: pipCommand not pinned by hash: .devcontainer/Dockerfile:51-55   (x3)
Warn: pipCommand not pinned by hash: Dockerfile:46-48                 (x3)
Warn: pipCommand not pinned by hash: docker/Dockerfile:34
Warn: npmCommand not pinned by hash: docker/Dockerfile:31
Warn: downloadThenRun not pinned by hash: docker/Dockerfile:24-28

Why this is not a one-liner

pip install --require-hashes is all-or-nothing: once any hash is supplied, every install requirement including transitive ones must carry a hash. That means generating a fully hashed constraints file, and this repo's installs are awkward for it:

  • Dockerfile:46-48 installs torch from https://download.pytorch.org/whl/cpu, a second index, then .[postgresql] from the local tree.
  • .devcontainer/Dockerfile:51-55 installs exact versions already (torch==2.11.0, sentence-transformers==5.4.1, flashrank==0.2.10) but exact versions are not hashes and do not satisfy the check.
  • docker/Dockerfile:24-28 is the NodeSource setup_22.x script piped into bash, which is the downloadThenRun finding. Pinning it means vendoring the key and apt source rather than hashing the script.
  • docker/Dockerfile:31 is npm install -g @anthropic-ai/claude-code, deliberately unversioned so the image tracks the current CLI.

There is a real trade-off in each case, and uv.lock already exists as a source of truth that a hashed constraints file could be generated from.

Acceptance criteria

  • Every pipCommand, npmCommand and downloadThenRun warning above is either resolved, or documented in-file with the reason it is deliberately unpinned and what compensates for it.
  • Any hashed constraints file is generated from uv.lock by a committed script, not hand-maintained.
  • Dockerfile still passes the blocking Docker Smoke job in ci.yml (the bare-container DB-less contract).
  • docker/Dockerfile and .devcontainer/Dockerfile are built at least once as evidence, since no CI job builds either today.
  • Re-run Scorecard and quote the Pinned-Dependencies score, by field diff rather than by overall number.

Note

The .devcontainer and docker/ images have no CI build at all, so a regression in either is currently invisible. That gap is worth its own issue if this one does not close it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions