Skip to content

[COSY-926] Automate CVE remediation: gated weekly rebuild + Dependabot + smoke test - #99

Draft
carole-lavillonniere wants to merge 5 commits into
mainfrom
cosy-926-automate-cve-remediation
Draft

[COSY-926] Automate CVE remediation: gated weekly rebuild + Dependabot + smoke test#99
carole-lavillonniere wants to merge 5 commits into
mainfrom
cosy-926-automate-cve-remediation

Conversation

@carole-lavillonniere

@carole-lavillonniere carole-lavillonniere commented Aug 20, 2026

Copy link
Copy Markdown

Resolves COSY-926. Follows up on #98 and the thread with @jbeemster and @Pive01.

Why not just Dependabot

Almost nothing Trivy reports on this image lives in a dependency manifest. Of the 9 findings on 2026.7.2:

Source Count Fixed by
Go stdlib (toolchain compiled into service) 7 rebuilding on a newer golang:1.25-alpine
golang.org/x/net in vm/go.mod 1 a dependency bump
golang.org/x/crypto (GO-2026-5932, no fix published) 1 nothing, yet

So Dependabot could have fixed one of nine. The stdlib version isn't a manifest entry anywhere — a plain rebuild is the only thing that moves it. Hence: rebuild automation does the heavy lifting, Dependabot covers the go.mod tail.

What's here

1. Weekly gated rebuild.github/workflows/security-rebuild.yml

Tuesdays 08:00 UTC, ~22h ahead of the secops Wednesday 06:00 UTC scan so anything cleared here never gets filed. Scan published image → rebuild --pull --no-cache → smoke test → rescan → publish only if before - after is non-empty, bumping the patch version. A rebuild that clears nothing is a no-op, so no update badge appears in Docker Desktop for a release that changes nothing (Luca's point in the thread).

Modelled closely on the security-rebuild job in localstack-pro's aws_flink.yml, including the Trivy DB mirrors for the GHCR rate limit.

Two deliberate choices:

  • --severity HIGH,CRITICAL --ignore-unfixed, matching what the secops pipeline files. GO-2026-5932 has no fix available and was correctly not in BEE-1115; without --ignore-unfixed it would make the job churn forever on something unfixable.
  • New CVEs are reported, not gated on. Refreshing to newer upstream rarely introduces CVEs, and when it does we'd still rather ship the net improvement. Logged as a workflow warning.
  • If a rebuild clears nothing, the remaining CVEs need a dependency bump, so the job says so in the step summary rather than silently no-op'ing every week until the SLA on the secops ticket burns.

2. Dependabot, gomod only.github/dependabot.yml

open-pull-requests-limit: 0 disables scheduled version updates and leaves security updates flowing. Scoped to vm/go.mod because that's the only manifest whose contents Trivy can see — the UI ships as an esbuild bundle with no node_modules, so npm advisories never reach the published image. (There are 38 open npm alerts on this repo today, 24 of them axios; none of them can close a security ticket.)

No automerge. These get reviewed and tested by hand, then released via workflow_dispatch. Test on the PR branch, before merging — if you test after merging, the next weekly rebuild sees "CVE count dropped" and auto-releases the bump without the test ever having run.

3. Smoke testscripts/smoke-test.sh, make smoke-test

The rebuild publishes unattended, so it should at least confirm the image works. Checks the three things a rebuild could plausibly break: /service starts and creates its socket, every host binary declared in metadata.json is present and executable, and the UI bundle and compose/metadata files were copied in. No Docker Desktop required.

4. PR CI.github/workflows/pr.yml

Runs make smoke-test on every PR: builds the image and verifies it starts and ships what it declares. There was no PR CI in this repo at all before.

This is what makes a dependency bump mergeable. Nothing auto-merges, so it isn't a safety gate — it's there so the bumps Dependabot raises can be verified without a local build, on the branch, before the merge. That ordering matters: test after merging and the next weekly rebuild sees the CVE count drop and releases the bump with the test never having run.

Uncached and amd64-only, ~50s. The weekly rebuild stays the uncached multi-arch build.

5. Pin FROM alpinealpine:3.24

Was fully unpinned, so an unattended rebuild could have jumped an Alpine major. apk upgrade --no-cache still pulls patches within 3.24, so security fixes keep flowing.

Note golang:1.25-alpine stays floating on purpose — that tag is exactly the mechanism that delivers stdlib patches.

6. Version bump scriptedscripts/bump-version.sh

The version lives in three places that have drifted: Makefile TAG, org.opencontainers.image.version in the Dockerfile (stale at 0.3.1 for many releases — this fixes it), and CHANGELOG.md. Dependabot can't touch any of them, so the release step owns them.

Verified locally

  • Built the image from this branch: picks up go1.25.14, 0 fixable HIGH/CRITICAL (2026.7.2 had 8).
  • Smoke test passes on that build, and fails as expected when a declared host binary is removed.
  • bump-version.sh produces 2026.8.02026.8.1 correctly across all three files.
  • PR CI is green on this PR, so the build + smoke-test path is exercised for real.
  • Not yet exercised: the publish path (needs the scheduled run / workflow_dispatch).

Note the job would no-op today2026.8.0 already has 0 fixable HIGH/CRITICAL after #98, which is the intended behaviour.

Deliberately not here

  • CLI pinning. downloadBinaries.sh still fetches localstack-cli from /releases/latest/, so a rebuild can pick up a new CLI. Carole's call — flagging only because the CLI did ship monthly releases through 2026 (latest v2026.7.0, 2026-07-22), so this isn't strictly frozen.
  • lstk migration — separate ticket, and not a drop-in: the extension calls start --no-banner -d and update docker-images, and lstk's start has only --persist while lstk update updates lstk itself. Also worth knowing that lstk being Go would make all five bundled platform binaries newly visible to Trivy, where the current PyInstaller ones are invisible.

carole-lavillonniere and others added 3 commits August 20, 2026 16:20
Add a gated weekly security rebuild, scope Dependabot to security updates
for vm/go.mod, and add a smoke test the rebuild must pass before it ships.

Most CVEs on this image come from the Go toolchain baked into the service
binary rather than a dependency manifest, so they are fixed by rebuilding
rather than by bumping anything. The rebuild only republishes when it
actually clears a CVE, so a no-op never surfaces as an update in Docker
Desktop.
Dependency bumps are the only way to clear CVEs a rebuild cannot, but nothing
verified them before a merge. Run make smoke-test on every PR so a bump is
tested on its branch rather than after it lands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@carole-lavillonniere
carole-lavillonniere force-pushed the cosy-926-automate-cve-remediation branch from 424c8cb to be43c27 Compare August 24, 2026 13:11
carole-lavillonniere and others added 2 commits August 24, 2026 15:22
Builds are now always --pull --no-cache, in the Makefile rather than at each
call site: a cached base layer reproduces the old image and clears no CVE, and
that property matters for every build, not just the weekly rebuild's.

IMAGE and TAG were already overridable, so the rebuild's separate buildx call
and script invocation collapse into make smoke-test IMAGE=dde-candidate
TAG=scan, leaving scripts/smoke-test.sh with a single caller. build-extension
already guards the binary download, so that step goes too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
no-cache on build-push-docker.yml put every push to main and every tag through
an uncached QEMU arm64 build, on the path you wait on when shipping a security
fix. pull: true invalidates the downstream layers whenever a base digest moves,
which is what the freshness actually depends on, and the weekly rebuild covers
the apk upgrade layer that only no-cache can re-run.

Restores the gha cache: without it the runner has nothing to import, so
dropping no-cache alone would have left the build cold anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant