diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 323e42e00..e102a12c9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -85,8 +85,11 @@ updates: - '@bsv/*' # Keep deployment manifests current. Runtime Dockerfiles are digest-only, so - # ignore their bare `node` dependency here: a digest-only Dependabot update - # would incorrectly resolve the registry's `latest` tag. + # exclude them from this scan: Dependabot treats their bare `node` dependency + # as `latest`, even when it is ignored, and its multi-directory updater then + # fails instead of producing a reviewable change. The readable, versioned + # Node source under /governance owns base-image discovery; deployment image + # and database updates remain in this entry. - package-ecosystem: docker directories: - /infra @@ -104,7 +107,11 @@ updates: - '*' multi-ecosystem-group: monthly-stack-maintenance rebase-strategy: auto + exclude-paths: + - Dockerfile + - '**/Dockerfile' ignore: + # Defense in depth if GitHub changes exclude-path handling. - dependency-name: node # Database majors require backup/restore, upgrade-path, rollback, and # application compatibility validation. Patch and minor refreshes remain diff --git a/docs/reference/container-supply-chain.md b/docs/reference/container-supply-chain.md index 3877b2219..8dbc834e9 100644 --- a/docs/reference/container-supply-chain.md +++ b/docs/reference/container-supply-chain.md @@ -1,10 +1,10 @@ --- id: container-supply-chain -title: "Container Supply Chain" +title: 'Container Supply Chain' kind: reference -version: "1.1.0" -last_updated: "2026-07-26" -last_verified: "2026-07-26" +version: '1.1.0' +last_updated: '2026-07-26' +last_verified: '2026-07-26' review_cadence_days: 30 status: stable tags: [reference, infrastructure, containers, security, releases] @@ -39,9 +39,13 @@ its digests let Scorecard verify pinning. Repository health requires those tag-and-digest references, the registry's expected version and digest, and every digest-only release `FROM` instruction to reconcile in one change. Runtime Dockerfiles deliberately omit the tag because Docker uses the digest as the -actual identity. Automatic Sonar analysis excludes only the non-build metadata -file because its rule rejects tag-and-digest syntax; the executable zero-install -repository check remains authoritative for it. +actual identity. They are excluded from the deployment-image Dependabot scan so +its Docker updater cannot reinterpret bare digest-only Node references as +`latest`; the versioned governance manifest is the single Node discovery +source, while deployment images and database versions remain monitored. +Automatic Sonar analysis excludes only the non-build metadata file because its +rule rejects tag-and-digest syntax; the executable zero-install repository check +remains authoritative for it. Package locks under `infra/**/package-lock.json` are committed release inputs. Release workflows never rewrite them. A stale or inconsistent lock therefore diff --git a/scripts/container-supply-chain.test.mjs b/scripts/container-supply-chain.test.mjs index f6666f51b..d58a5f9e8 100644 --- a/scripts/container-supply-chain.test.mjs +++ b/scripts/container-supply-chain.test.mjs @@ -223,6 +223,11 @@ test('Docker refreshes and OpenSSF posture checks remain automated', () => { assert.match(dependabot, /package-ecosystem: docker/) assert.match(dependabot, /directory: \/governance/) assert.match(dependabot, /dependency-name: node/) + assert.match( + dependabot, + /exclude-paths:\s+- Dockerfile\s+- '\*\*\/Dockerfile'/, + 'Dependabot must not reinterpret digest-only runtime Node bases as the mutable latest tag' + ) for (const component of registry.components) { assert.match(dependabot, new RegExp(`- /${component.path.replaceAll('/', '\\/')}`)) }