Skip to content

feat: add SBOM and vulnerability scans for pinned runtime container images - #15657

Merged
atomantic merged 4 commits into
mainfrom
claim/issue-15656
Nov 10, 2026
Merged

atomantic merged 4 commits into
mainfrom
claim/issue-15656

Conversation

@atomantic

@atomantic atomantic commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds a Runtime image security workflow that produces an SBOM (syft) and a vulnerability scan (grype, OS packages and language deps) for the pinned runtime recipes the npm/Python audits cannot see.

  • Targets are derived from the recipes by scripts/runtime-image-security.js (no second copy of a digest): the digest-pinned pgvector image in docker-compose.yml (amd64 + arm64, pulled by digest), docker/supercollider/ (built with the product's own build argv, amd64 + arm64 on native runners), and the inline npm:@dimforge/rapier3d-compat@0.14.0 pin from the eidoverse-video recipe.
  • Each run uploads SBOM (syft + CycloneDX), grype result, grype DB status, findings.json and provenance.json (image ref/ID/repo digests, recipe path and hash, commit, run, architecture, tool versions, DB identity).
  • Fail-closed: scanner errors, an empty/malformed SBOM, a missing deb/npm ecosystem or a missing distro, or an unparseable scan fail the run; none read as zero findings.
  • Not part of CI Gate. Triggers: weekly schedule, workflow_dispatch, and PRs touching the recipes. Scheduled/dispatched runs are strict (fail on findings with no reviewed disposition in docker/runtime-image-security.reviews.json); PR runs only report, so an upstream advisory never blocks an unrelated PR.
  • Syft/grype binaries are pinned by version and archive SHA-256; actions by commit SHA (same pins as the existing workflows).
  • The eidoverse:render parent and its external deno.json/deno.lock are reported as unavailable (owning project named) in every run's summary and an artifact; never a pass.
  • The scheduled dependency-updates prompt (v11, integrity snapshot regenerated) now consumes this evidence (run age <= 14 days, recipe/digest match) or names the coverage gap.
  • Docs: docs/GITHUB_ACTIONS.md (new section), pointer from docs/SUPERCOLLIDER.md. Existing digest/version guards and the Debian snapshot are untouched.

Test plan

  • scripts/runtime-image-security.test.js (15 tests): derivation from the real recipes (including the PINNED_DB_IMAGE pin) and refusal of an unpinned recipe, fail-closed SBOM/scan validation, review scoping (target/package/version/arch), verifyRun provenance + strict exit, the unavailable report, and a workflow contract (not in CI Gate, SHA-pinned actions, checksummed scanners, every target x architecture on a native runner, validation before upload).
  • Prompt/regen-snapshot/dependabot-admission/pre-install suites pass; npm run pregate passes on the final head. Workflow YAML parsed, and actionlint is clean after the fix commit.
  • Not run locally: Docker pulls/builds and syft/grype (none installed, containment disabled). The first hosted run is the real proof of the scanner invocations.

Closes #15656

First hosted scan

The first push left GitHub rejecting the workflow file: runner.temp was used in job-level env, where the runner context is not available. Commit 9680328 sets EVIDENCE_DIR inside a step instead, and actionlint is now clean. The PR run that followed (run 42849197306) passed all 6 jobs and uploaded valid evidence for each target and architecture:

  • pgvector: 537 unreviewed matches (29 critical), amd64 and arm64 identical.
  • supercollider: 201 unreviewed (0 critical, 64 high).
  • inline Rapier pin: 0.

Follow-ups

Local review

2 of 2 rounds run with gpt-6.1-terra. Round 1 (at 48a2d35) had 3 findings, all accepted and fixed (review scope by version/arch with no "fixed" suppression, ledger test, evidence age bound). Round 2 (at 3ece657) had 1 finding (record the eidoverse recipe hash in provenance), accepted and fixed in the final head without a third review. Status partial. The orchestrator's later one-line workflow fix (9680328) was validated by actionlint and the hosted run, not by the local reviewer.

…mages (#15656)

A new Runtime image security workflow (weekly, on demand, and on PRs touching
the recipes; not part of CI Gate) builds or pulls the pgvector and SuperCollider
images on native amd64 and arm64 runners, generates a syft SBOM and a grype scan
including OS packages, and uploads both with provenance (image, digest, recipe
hash, commit, architecture, scanner and DB identity). Scanner errors or empty or
malformed output fail the run instead of reading as zero findings. The inline
Rapier npm pin is scanned too; the externally built eidoverse render parent and
its deno.lock are reported as unavailable with the owning project. The scheduled
dependency-updates prompt (v11) now consumes this evidence or names the gap.
GitHub rejected the workflow file because the runner context is not
available in job-level env.
@atomantic
atomantic enabled auto-merge November 10, 2026 16:00
@atomantic
atomantic merged commit 8fca2fb into main Nov 10, 2026
25 checks passed
@atomantic
atomantic deleted the claim/issue-15656 branch November 10, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

implementer:sonnet-5-5 reviewer:gpt-6-1-terra Locally reviewed by the gpt-6-1-terra model

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add security inventories for pinned runtime container images

1 participant