Skip to content

chore(ci): bump actions/checkout from 6.0.3 to 7.0.0 - #334

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/actions/checkout-7.0.0
Closed

chore(ci): bump actions/checkout from 6.0.3 to 7.0.0#334
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/actions/checkout-7.0.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jun 20, 2026

Copy link
Copy Markdown
Contributor

Bumps actions/checkout from 6.0.3 to 7.0.0.

Release notes

Sourced from actions/checkout's releases.

v7.0.0

What's Changed

New Contributors

Full Changelog: actions/checkout@v6.0.3...v7.0.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v7.0.0

v6.0.3

v6.0.2

v6.0.1

v6.0.0

v5.0.1

v5.0.0

v4.3.1

v4.3.0

v4.2.2

v4.2.1

... (truncated)

Commits

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file github-actions Pull requests that update GitHub Actions labels Jun 20, 2026
@dependabot
dependabot Bot requested a review from Cre-eD as a code owner June 20, 2026 13:31
@dependabot dependabot Bot added the dependencies Pull requests that update a dependency file label Jun 20, 2026
@dependabot dependabot Bot added the github-actions Pull requests that update GitHub Actions label Jun 20, 2026
@dependabot
dependabot Bot requested a review from Laboratory as a code owner June 20, 2026 13:31
@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown

Semgrep Scan Results

Repository: api | Commit: 15ae2a2

Check Status Details
⚠️ Semgrep Warning 1 warning(s), 5 total

Scanned at 2026-07-01 13:03 UTC

@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown

Security Scan Results

Repository: api | Commit: 15ae2a2

Check Status Details
✅ Secret Scan Pass No secrets detected
✅ Dependencies (Trivy) Pass 0 total (no critical/high)
✅ Dependencies (Grype) Pass 0 total (no critical/high)
📦 SBOM Generated 523 components (CycloneDX)

Scanned at 2026-07-01 13:03 UTC

@dependabot
dependabot Bot force-pushed the dependabot/github_actions/actions/checkout-7.0.0 branch from 7dccb96 to 6fa099d Compare June 21, 2026 11:47
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

📊 Statement coverage

Measured on the documented included set (see docs/TESTING.md → Coverage scope). Observe-only — no regression gate is enforced yet.

Scope This PR main baseline Δ
Included set (Gold-tier denominator) 90.3% 90.3% +0.0 pp
Full set (whole repo, transparency) 27.9% 27.9% +0.0 pp

Baseline: main @ 5e9f06e

@dependabot
dependabot Bot force-pushed the dependabot/github_actions/actions/checkout-7.0.0 branch from 6fa099d to 36182ac Compare June 25, 2026 08:40
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@df4cb1c...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot
dependabot Bot force-pushed the dependabot/github_actions/actions/checkout-7.0.0 branch from 36182ac to 598f2a9 Compare July 1, 2026 13:02
Cre-eD added a commit that referenced this pull request Aug 3, 2026
…lities 8 → 10) (#372)

## Summary

OpenSSF Scorecard's **Vulnerabilities** check is at **8/10** with 8 open
advisories. This PR takes it to **10/10**.

- **7 fixed by version bump** — 3 Go, 4 Python (all severities, nothing
deferred).
- **1 unfixable by design** — `GO-2026-5932` has `introduced: 0` and no
fix event in OSV. Declared `not_affected` with reachability evidence.
- **2 stale suppressions retired** — `GO-2022-0635` / `GO-2022-0646` no
longer apply; removed rather than carried forward.

`osv-scanner scan source -r .` → **No issues found** (was 8). Scorecard
runs the same scanner (`osvscanner.DoScan` via `clients/osv.go`) against
the repo root, so the check flips on merge to `main`.

## Fixed

| Package | Old → New | Advisory | Sev | Where |
|---|---|---|---|---|
| `github.com/klauspost/compress` | 1.18.4 → 1.18.7 | GO-2026-5841 /
GHSA-259r-337f-4rfw — OOB read in `s2` | HIGH | `go.mod` (indirect) |
| `google.golang.org/grpc` | 1.81.1 → 1.82.1 | GO-2026-6061 /
GHSA-hrxh-6v49-42gf — xDS RBAC + HTTP/2 server | HIGH | `go.mod`
(indirect) |
| `click` | 8.1.8 → 8.4.2 | PYSEC-2026-2132 / CVE-2026-7246 | MEDIUM |
`docs/requirements.txt` |
| `pymdown-extensions` | 10.21.3 → 11.0.1 | GHSA-9xwg-3r6f-jcx2 /
CVE-2026-61632 — `b64` path traversal | MEDIUM | `docs/requirements.txt`
|
| `setuptools` | 82.0.1 → 83.0.0 | PYSEC-2026-3447 / CVE-2026-59890 |
MEDIUM | `docs/requirements.txt` |
| `soupsieve` | 2.8.3 → 2.9.1 | PYSEC-2026-3071 / CVE-2026-49476 —
memory exhaustion | HIGH | `docs/requirements.txt` |
| `soupsieve` | 2.8.3 → 2.9.1 | PYSEC-2026-3072 / CVE-2026-49477 — ReDoS
in selector parser | HIGH | `docs/requirements.txt` |

Side-effect bumps pulled in by the above: `x/crypto` 0.53.0 → 0.54.0,
`x/sys` 0.46.0 → 0.47.0, `x/term` 0.44.0 → 0.45.0,
`opentelemetry-operations-go/detectors/gcp` 1.31.0 → 1.32.0,
`contrib/detectors/gcp` 1.42.0 → 1.43.0, plus the `tools.go` chain (see
*CI ordering* below).

### How the Python side was done

`docs/requirements.in` gains explicit patched floors for the four
packages, following the pattern already established for `requests` /
`urllib3` after #264 — pinning the floor in the `.in` file is what stops
a future `pip-compile` from silently resolving back to a vulnerable
version. `docs/requirements.txt` was then regenerated with `pip-compile
--allow-unsafe --generate-hashes --upgrade`.

`mkdocs-material` 9.7.7 declares `pymdown-extensions>=10.2` with no
upper bound, so the 11.x major is in range for the theme — no theme pin
change needed, and the docs site builds clean (evidence below). The
`b64` extension that carries CVE-2026-61632 is not enabled in
`docs/mkdocs.yml` at all; the bump closes the advisory rather than a
live exposure.

## Not fixable — `GO-2026-5932` (`golang.org/x/crypto/openpgp`)

> The `golang.org/x/crypto/openpgp` package is unmaintained, unsafe by
design, and has known security issues.

This is a permanent "this package should not be used" notice, not a
patchable defect. In OSV it is `introduced: 0` with **no fix event**, so
no version of `x/crypto` clears it — bumping to 0.54.0 (done here
anyway, for hygiene) changes nothing.

**Reachability:**

1. **Where in the tree** — `x/crypto` is a direct dependency, required
for `chacha20poly1305` in `pkg/api/secrets/ciphers`. The advisory covers
only `openpgp` and its six subpackages.
2. **Is it in the artifact** — no. `go list -deps ./... | grep
x/crypto/openpgp` returns nothing, and Go's linker does not emit
packages no import path reaches. The vulnerable code is not present in
any binary this repo produces.
3. **Is the vulnerable code called** — no. `govulncheck -mode=source
./...` reports **0 reachable vulnerabilities**; this advisory lands in
its "modules you require, but your code doesn't appear to call" bucket.
4. **What OpenPGP work actually runs** —
`github.com/ProtonMail/go-crypto/openpgp`, which is the maintained fork
the advisory text itself recommends as the replacement.
5. **What would change the answer** — any new import of
`x/crypto/openpgp/*`. That would flip the VEX statement to `affected`
and block the gate.

Recorded as `status: not_affected`, `justification:
vulnerable_code_not_present` in [`vex/openvex.json`](vex/openvex.json),
mirrored into [`osv-scanner.toml`](osv-scanner.toml) — Scorecard's check
has no VEX input, so the mirror is the only channel that reaches it. No
`.trivyignore`, no `# nosec`, nothing hidden.

## Re-triage of existing suppressions

Both entries that predate this PR were re-checked rather than carried
forward:

| Advisory | Was | Now | Why |
|---|---|---|---|
| GO-2022-0635 (aws-sdk-go v1 s3crypto) | `not_affected` + ignore |
`fixed`, ignore removed | `github.com/aws/aws-sdk-go` (v1) is no longer
in `go.mod` — the Pulumi upgrades dropped the last build-graph edge and
Go module pruning removed it. `osv-scanner` reported the entry as an
**unused ignore**. |
| GO-2022-0646 (same subpackage) | `not_affected` + ignore | `fixed`,
ignore removed | Same. |

VEX statements are kept one release as an audit trail; the
`osv-scanner.toml` entries are gone.

## Governance changes

- **CODEOWNERS** — `/vex/` and `/osv-scanner.toml` are now named
explicitly. A `not_affected` statement hides a real advisory from both
the scanners and the Scorecard badge; that is the same class of change
as touching a workflow, and it should not merge on a generic `*` match.
- **`docs/DEPENDENCIES.md`** — documents (a) that `osv-scanner.toml` is
a *derivative* mirror which may only carry an ID that already exists in
VEX, and (b) that every SCA pass re-triages both files, with `unused
ignores` as the removal signal. Previously the policy said suppressions
live "never in a scanner-suppression file" while `osv-scanner.toml`
existed; policy and practice now agree.
- **`osv-scanner.toml`** — added a note that `reason` must stay a
single-line TOML basic string. A newline in it makes osv-scanner discard
the **entire** config (`strings cannot contain newlines`) and silently
re-report every ignored advisory. Hit while writing this PR.

## CI ordering (`tools.go` pre-bake)

`build-setup` runs `go get $(tools.go imports)` → `go mod download` →
`go generate -tags tools` → `go mod tidy`. Because `go get` resolves
tools to *latest* and `tidy` only runs afterwards, a tool minor landing
between runs breaks `go generate` with `missing go.sum entry`. The
post-`go get` state is pre-baked into this commit so CI's `go get` is a
no-op: `go-jsonschema` 0.23.1 → 0.24.1, `go-internal` 1.14.1 → 1.15.0,
`gofumpt` 0.10.0 → 0.11.0, `x/mod` 0.37.0 → 0.38.0, `x/net` 0.56.0 →
0.57.0, `x/tools` 0.47.0 → 0.48.0, `x/telemetry` bumped.

## Dependabot reconciliation

Open alerts before this PR: 5 — `pymdown-extensions`, `setuptools`,
`soupsieve` ×2, `grpc`. All 5 are closed by the bumps above; they
auto-resolve once this lands on `main`. `click` (PYSEC-2026-2132) and
`klauspost/compress` (GO-2026-5841) were **not** alerted by Dependabot
but are flagged by OSV — fixed here too.

Open Dependabot PRs:

| PR | Disposition |
|---|---|
| #361 `soupsieve 2.8.3 → 2.8.4` | Superseded — this PR goes to 2.9.1.
Dependabot closes it automatically when the manifest lands. |
| #371, #352, #351, #334, #333, #326 | Untouched — `github-actions` /
`docker` streams, unrelated to this PR. |

No config change needed: `.github/dependabot.yml` already covers `gomod`
(/), `pip` (/docs), `docker`, and `github-actions`.

## Evidence

<details>
<summary><code>osv-scanner scan source -r .</code> — before</summary>

```
click              8.1.8    [PyPI] -> PYSEC-2026-2132
pymdown-extensions 10.21.3  [PyPI] -> GHSA-9xwg-3r6f-jcx2
setuptools         82.0.1   [PyPI] -> PYSEC-2026-3447, GHSA-h35f-9h28-mq5c
soupsieve          2.8.3    [PyPI] -> PYSEC-2026-3071, PYSEC-2026-3072,
                                      GHSA-2wc2-fm75-p42x, GHSA-836r-79rf-4m37
klauspost/compress 1.18.4   [Go]   -> GO-2026-5841
golang.org/x/crypto 0.53.0  [Go]   -> GO-2026-5932
google.golang.org/grpc 1.81.1 [Go] -> GO-2026-6061, GHSA-hrxh-6v49-42gf

osv-scanner.toml has unused ignores:
 - GO-2022-0635
 - GO-2022-0646
```
</details>

<details>
<summary><code>osv-scanner scan source -r .</code> — after</summary>

```
Scanned docs/requirements.txt file and found 36 packages
Scanned go.mod file and found 486 packages
Loaded filter from: osv-scanner.toml
GO-2026-5932 has been filtered out because: VEX not_affected
  (vulnerable_code_not_present) ...
Filtered 1 vulnerability from output

No issues found
```
</details>

<details>
<summary><code>govulncheck -mode=source ./...</code> — after</summary>

```
=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 0 vulnerabilities in packages you import and 1
vulnerability in modules you require, but your code doesn't appear to call
these vulnerabilities.
```

The 1 module-level finding is GO-2026-5932, covered above.
</details>

## Test plan

- [x] `go build ./...`
- [x] `go generate -tags tools` (post-pre-bake, matches CI's build-setup
order)
- [x] `go test ./pkg/api/secrets/... ./pkg/security/... -count=1` — all
pass
- [x] Docs built exactly as CI does it: `docker run --rm -v
$PWD/docs:/docs -w /docs python@sha256:401f6e1a... sh -c "pip install
--require-hashes -r requirements.txt && mkdocs build"` → `Documentation
built in 3.39 seconds`, hash check clean, no new warnings
- [x] `jq empty vex/openvex.json`
- [ ] CI: govulncheck, CodeQL, Semgrep, build matrix

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
@Cre-eD

Cre-eD commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Superseded by #377, a consolidated SCA pass that includes this bump. #377 goes to v7.0.1 rather than v7.0.0 - v7.0.0 predates the --unset escaping and default-input fixes.

Closing to keep the queue consistent with what landed. Full before/after scan evidence and per-finding reachability analysis are in #377.

@Cre-eD Cre-eD closed this Aug 10, 2026
@dependabot @github

dependabot Bot commented on behalf of github Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot
dependabot Bot deleted the dependabot/github_actions/actions/checkout-7.0.0 branch August 10, 2026 15:31
Cre-eD added a commit that referenced this pull request Aug 11, 2026
…nd CI pins (#377)

## Summary

Full SCA pass over source, all 5 published container images, and the CI
pin surface. Consolidates the **7 open Dependabot PRs** into one review
and adds 5 findings none of them could see.

| | Fixed | Deferred | Documented unfixable |
|---|---|---|---|
| CRITICAL | 0 | 0 | 0 |
| HIGH | 1 source + 102 image | 0 | 8 |
| MEDIUM | 1 source + 32 image | 0 | 4 |
| LOW | 0 + 1 image | 0 | 0 |

Locally-built images, `origin/main` vs this branch, identical build
inputs:

| image | trivy (main → branch) | grype `--only-fixed` (main → branch) |
|---|---|---|
| `kubectl` | 21 → **8** | 39 → **11** |
| `caddy` | 28 → **3** | 37 → **9** |
| `cloud-helpers` | 64 → **0** | 27 → **4** |
| `github-actions` | 34 → **1** | 44 → **5** |
| **total** | **147 → 12** (−92%) | **147 → 29** (−80%) |

Every one of the 12 remaining findings sits inside a third-party
prebuilt binary already at its newest available version. Nothing fixable
is left unfixed, and no suppression was added.

> **Read Round 2 at the bottom too.** A six-model review panel plus two
real preview builds followed this round; it corrected two defects in the
diff below (cache scoping, and a weakened pin in `welder.yaml`) and
found three pre-existing ones, including a Pulumi checksum gate that
could not fail and a published image that cannot start.

## Fixed

| Package | Old → New | CVE / advisory | Sev | Source |
|---|---|---|---|---|
| `github.com/go-git/go-git/v5` | 5.19.1 → 5.19.2 | CVE-2026-71556
(`GHSA-hc8v-wwc9-vgxm`) worktree ops follow symlinks | HIGH | trivy fs,
osv-scanner, Dependabot alert #75 |
| `github.com/go-git/go-git/v5` | 5.19.1 → 5.19.2 | CVE-2026-71557
(`GHSA-qgq7-7hm3-q39j`) malicious ref names write outside ref storage |
MEDIUM | trivy fs, osv-scanner, Dependabot alert #76 |
| Caddy binary (`xcaddy build`) | v2.11.3 → v2.11.4 | CVE-2026-52844,
CVE-2026-52845 (HIGH), CVE-2026-52846 (MEDIUM) | H×2 M×1 | trivy image |
| `alpine` base (×4 stages) | 3.23 → 3.24 | 30 distro CVEs → 0; also
lifts `py3-cryptography` 46.0.7 → 47.0.0 | H2 M8 L20 | trivy image |
| `caddy:2.11.4` + `-builder` | digest refresh | 55 → 25 in the base
layer | H12 M23 L20 | trivy image |
| `alpine/kubectl:latest` | digest refresh | 68 → 8 in the base layer |
H20 M28 L20 | trivy image |
| `lambda/provided:al2023` | digest refresh | 64 → 0 in the base layer |
H58 M5 L1 | trivy image |
| gcloud CLI | 567.0.0 → 579.0.0 | rolls up vendored Go deps in the
bundled binaries; SHA-256 recomputed from the real tarball | — | trivy
image |
| `python:3.9.18` → `python@sha256:229a2c5b` (3.12-slim) (welder
`build-docs`) | EOL runtime | 10110 → 213 image vulns (C248→4, H2293→23)
| C/H/M/L | trivy image |

### CI pin bumps

`actions/checkout` v6.0.3 → **v7.0.1** (Dependabot proposed v7.0.0;
v7.0.1 adds the `--unset` escaping and default-input fixes) ·
`actions/cache` + `actions/cache/save` v5.0.5 → **v6.1.0** ·
`actions/setup-go` v6 → **v7.0.0** · `actions/attest-build-provenance`
v4.1.0 → **v4.2.2** · `docker/setup-buildx-action` v4.1.0 → **v4.2.0** ·
`docker/build-push-action` v7.2.0 → **v7.3.0** · `ossf/scorecard-action`
v2.4.3 → **v2.4.4** · `github/codeql-action` v4 SHA refresh (comment
corrected to `# v4.37.6` in round 2).

All SHAs resolved through the GitHub git-ref API, annotated tags
dereferenced to commits. Compatibility checked before applying:

- **ESM majors are safe here.** `checkout` v7, `cache` v6 and `setup-go`
v7 are all "migrate to ESM" releases. Both the old and new versions
declare `using: node24`, so no runner requirement changes on
`blacksmith-8vcpu-ubuntu-2204`, `ubuntu-24.04` or `ubuntu-latest`.
- **`checkout` v7's fork-PR block does not bite.** v7 refuses to check
out fork PR code under `pull_request_target` / `workflow_run` unless
`allow-unsafe-pr-checkout` is set. This repo has no
`pull_request_target` at all; the three `workflow_run` workflows either
do not check out (`security-scan-comment.yml`, `semgrep-comment.yml`) or
check out the base repo with no `ref:` (`verify-attestations.yml`). The
one PR-head checkout (`dco.yml`, `ref:
github.event.pull_request.head.sha`) runs on `pull_request`, which the
block does not cover.

### Stale pin comment

`build-staging.yml` pinned `actions/cache@27d5ce7f` with a `# v4.3.0`
comment, while `push.yaml` annotated the *same* SHA as `# v5.0.5`. The
GitHub tag API confirms 27d5ce7f is **v5.0.5**; the v4.3.0 comment was
wrong and made the pin look two majors older than it was. Corrected as
part of the bump.

## Two findings Dependabot could not see

### 1. `caddy.Dockerfile` shipped a binary one patch behind its own base
image

The file header says *"Bumping requires editing all three '2.11.x' sites
below (two FROMs + xcaddy)"*. Both `FROM` lines were on `caddy:2.11.4`,
but `xcaddy build "v2.11.3"` was left behind, and the built binary is
what `COPY --from=builder /usr/bin/caddy` puts in the final image. So
the shipped Caddy was 2.11.3 and still carried CVE-2026-52844 +
CVE-2026-52845. Dependabot tracks `FROM` lines, not `xcaddy` arguments,
so this class of drift is invisible to it. Verified after the fix:

```
$ docker run --rm --entrypoint sh <built> -c 'cat /etc/alpine-release; caddy version; caddy list-modules | grep -E "rate_limit|gcs"'
3.23.5
v2.11.4 h1:XKxkMTgNSizEvKG6QHue6cAsFOteU2qA61w2tKkCWi0=
caddy.storage.gcs
http.handlers.rate_limit
```

### 2. Published images do not match the Dockerfiles that supposedly
built them

`simplecontainer/caddy:latest` is byte-identical to `:2026.8.3` (both
`sha256:83b0e6bb…`, pushed 2026-08-10 11:52 UTC, today). What is inside
it:

```
$ docker run --rm --entrypoint sh simplecontainer/caddy:latest -c \
    'cat /etc/alpine-release; caddy version; ls -la /usr/bin/caddy; caddy list-modules | grep -E "rate_limit|gcs"'
3.20.4
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
-rwxr-xr-x 1 root root 41582744 Apr 27 08:56 /usr/bin/caddy
caddy.storage.gcs
```

Three things are wrong at once: alpine **3.20.4** where the pinned base
resolves to 3.23.5, Caddy **v2.8.4** where the Dockerfile builds 2.11.x,
a `/usr/bin/caddy` timestamped **Apr 27**, and
**`http.handlers.rate_limit` absent**. That last one is decisive:
`caddy.Dockerfile` asserts on that module at build time (`caddy
list-modules | grep -qE '^http\.handlers\.rate_limit$'`), so the
published image cannot be the output of that Dockerfile. Its `apk`
database also reports alpine 3.20.4 while a fresh build of the same file
reports 3.23.5.

Cost of that in the artifact consumers actually pull (`trivy image`,
published `:latest` tags):

| published image | C | H | M | L | total |
|---|---|---|---|---|---|
| `simplecontainer/caddy:latest` | **6** | 69 | 68 | 27 | 170 |
| `simplecontainer/github-actions:latest` | **2** | 67 | 51 | 8 | 128 |
| `simplecontainer/cloud-helpers:aws-latest` | 0 | 73 | 13 | 1 | 87 |
| `simplecontainer/kubectl:latest` | 0 | 21 | 38 | 24 | 83 |

Among caddy's 6 CRITICALs: `smallstep/certificates` v0.26.1
(CVE-2025-44005, CVE-2026-30836), `grpc` v1.64.0 (CVE-2026-33186),
`libcrypto3`/`libssl3` 3.3.2-r1 (CVE-2026-31789), Go `stdlib` v1.23.4
(CVE-2025-68121) — plus the four Caddy admin-socket / fastcgi
auth-bypass HIGHs (CVE-2026-27586/27587/27588/27590) that
`caddy.Dockerfile`'s own header claims to have closed. This matters more
than usual because the SC composite actions run these images by floating
tag rather than digest, so consumers inherit whatever `:latest` points
at.

**Probable mechanism, and the change here.** Every
`docker/build-push-action` step in the repo used `cache-from: type=gha`
/ `cache-to: type=gha,mode=max` with **no `scope:`**, so all builds
shared one cache namespace: the 5-way image matrix in `push.yaml` (all
writing `mode=max` concurrently), both images in `build-staging.yml`,
and the preview matrix in `branch-preview.yaml`. Concurrent `mode=max`
writers into a single GHA cache scope is a known way to get cache-index
collisions across unrelated Dockerfiles. This PR scopes the cache per
build — see **Round 2** below, which corrects the first attempt at this:
scoping per *image* still let `push.yaml` and `branch-preview.yaml`
collide on `github-actions`, so the final design keys scopes by **trust
tier**.

I want to be precise about confidence: the *observation* above is proven
by direct execution, and unscoped shared cache is the most plausible
mechanism, but I have not reproduced the collision inside GitHub's cache
backend. If the next release still publishes a stale caddy after this
lands, the next place to look is the welder image-build path rather than
buildx. Either way scoping the cache is correct on its own merits.

## Documented unfixable (12 remaining, 0 CRITICAL)

Every residual finding lives in a third-party prebuilt binary that we do
not compile, each already at the newest published version. No
`.trivyignore`, no VEX statement added for any of these — a scanner
finding against a current upstream artifact is honest signal and should
keep showing until upstream ships.

**`/usr/local/bin/kubectl` (8) — upstream Kubernetes v1.36.3.**
`golang.org/x/net` v0.49.0 (CVE-2026-25681, CVE-2026-27136,
CVE-2026-39821, CVE-2026-33814 HIGH; CVE-2026-25680, CVE-2026-42502,
CVE-2026-42506 MEDIUM) and `golang.org/x/text` v0.33.0 (CVE-2026-56852).
*Reachability:* `kubectl` here is a CI-driven CLI speaking TLS to a
trusted, authenticated API server endpoint; the x/net HTTP/2 and proxy
parsing paths need a hostile server or hostile URL, and the x/text issue
is an infinite loop on invalid input (DoS of a short-lived CI process).
`alpine/kubectl:latest` is already the newest digest. *Closes when:*
upstream k8s rebuilds against x/net ≥ 0.55.0.

**`/usr/bin/caddy` (3) — vendored into Caddy v2.11.4's own module
graph.** `x/text` v0.37.0 (CVE-2026-56852), `grpc` v1.81.0
(`GHSA-hrxh-6v49-42gf`, xDS RBAC fail-open + HTTP/2), `cel-go` v0.28.1
(`GHSA-gcjh-h69q-9w9g`, `json:"-"` fields readable from CEL).
*Reachability:* Caddy is genuinely internet-facing, so these deserve the
least benefit of the doubt — but both non-`x/text` findings sit in
features this build does not use. The grpc advisory is scoped to
`internal/xds/rbac` and the HTTP/2 *server* transport; Caddy links grpc
as a client for its own internals and has no xDS RBAC authorization
engine. `cel-go` is reached only through
`ext.NativeTypes(ParseStructTag("json"))` over user-submitted CEL, which
requires a CEL expression surface this build does not expose (the plugin
set is `certmagic-gcs` + `caddy-ratelimit`). Overriding Caddy's module
graph via xcaddy would mean shipping a Caddy built against dependency
versions upstream has not tested, which trades a narrow unreachable
finding for a broad untested one. *Closes when:* Caddy 2.11.5+ ships.

**`/opt/google-cloud-sdk/bin/gke-gcloud-auth-plugin` (1) — Google-built,
gcloud 579.0.0 (newest).** `x/text` v0.38.0, CVE-2026-56852.
*Reachability:* a short-lived credential helper parsing Google's own
token responses, not attacker-supplied text; the advisory is an infinite
loop on invalid input. *Closes when:* Google rebuilds the plugin against
x/text ≥ 0.39.0.

**`/usr/local/bin/aws-lambda-rie` (4, grype only) — AWS-built, part of
`lambda/provided:al2023`.** `x/sys` v0.21.0 (`GO-2026-5024`),
`go-chi/chi/v5` v5.2.4 (`GO-2026-5774/5775/5777`). *Reachability:* the
Runtime Interface Emulator is a local-testing shim AWS ships in the base
image; it is not the entrypoint (`ENTRYPOINT ["/cloud-helpers"]`) and
never runs in Lambda. *Closes when:* AWS refreshes the base image.

**`py3-cryptography` 47.0.0 (4, grype only) — Alpine's newest, in
`github-actions`.** `GHSA-537c-gmf6-5ccf`, `GHSA-jwv3-5hgf-82ww`,
`GHSA-g6cj-pr64-35w5` (HIGH), `GHSA-m2h6-j472-rp4c` (MEDIUM). The fixes
need 48.0.1 / 49.0.0 / 50.0.0, and Alpine ships 47.0.0-r0 on **both 3.24
and edge** — there is no branch to bump to. *Reachability:* pulled in by
apk-managed `aws-cli` and used by gcloud auth; the PKCS#7 Bleichenbacher
oracle and the `permittedSubtrees` wildcard escape need
attacker-controlled PKCS#7 blobs or hostile certificate chains, neither
of which is on the CI credential path. *Why not force it:* `pip install
cryptography>=48` over the apk package would break Alpine's
`externally-managed-environment` marker and desynchronise the
`aws-cli`/gcloud dependency set — worse than the finding. The 3.23 →
3.24 bump in this PR already moved it 46.0.7 → 47.0.0. *Closes when:*
Alpine packages 48.0.1+.

Note this is a real trivy/grype split worth knowing: trivy scores
`py3-cryptography` as an Alpine package with no distro fix and stays
quiet, grype catalogues the same files as a PyPI dist-info and matches
the GHSA advisories. Neither is wrong. Both tools are run here for
exactly this reason.

## VEX re-triage

`vex/openvex.json` v2 → v3. Nothing was added; two statements were
corrected and one re-confirmed.

| advisory | was | now | why |
|---|---|---|---|
| GO-2022-0635 (CVE-2020-8911) | `fixed` | `not_affected` /
`component_not_present` | see below |
| GO-2022-0646 (CVE-2020-8912) | `fixed` | `not_affected` /
`component_not_present` | see below |
| GO-2026-5932 (x/crypto/openpgp) | `not_affected` /
`vulnerable_code_not_present` | unchanged, evidence re-verified | grep
and call-path evidence still hold; `x/crypto` v0.54.0 is the latest
release |

**The two `fixed` statements were not fixable, and that made them
indefinite suppressions.** OSV lists both aws-sdk-go advisories with
`{"introduced": "0"}` and **no fix event** — AWS never patched the v1
`s3crypto` client, the remediation is migrating to the V2 encryption
client. So `fixed` described a state that could never be reached, while
still suppressing the finding in any VEX-aware scanner. The prior
`impact_statement` also asserted the module had "left `go.mod` entirely
through Go module pruning", which is not what the tree shows:

```
$ go list -m all | grep '^github.com/aws/aws-sdk-go '
github.com/aws/aws-sdk-go v1.55.8              # present in the requirement graph
$ go list -deps ./... | grep -c '^github.com/aws/aws-sdk-go/'
0                                              # zero packages linked into any binary
$ go mod why -m github.com/aws/aws-sdk-go
(main module does not need module github.com/aws/aws-sdk-go)
```

`component_not_present` is the accurate justification: the module is a
requirement-graph edge from a transitive `go.mod`, no package from it
reaches the linker, and `s3crypto` specifically is never imported (S3
goes through aws-sdk-go-v2). Neither `govulncheck -mode=source` nor
`osv-scanner` reports these against the repo, so no `osv-scanner.toml`
mirror entry is needed; the header comment there is updated to say why
they stay out and under what condition they would come back.

`osv-scanner.toml` keeps exactly one `IgnoredVulns` entry
(GO-2026-5932), unchanged. `CODEOWNERS` already gates `/vex/` and
`/osv-scanner.toml`.

## Dependabot reconciliation

All 7 open bot PRs are superseded by this one. Note
`.github/dependabot.yml` is deliberately configured so majors fall
outside the version groups and open individual PRs "for isolated
review/testing" — that config is working as designed and is left
untouched; this PR is the isolated review.

| PR | What it proposed | Action | Why |
|---|---|---|---|
| #374 | `go-git/v5` 5.19.1 → 5.19.2 (`gomod-security`) | superseded |
identical bump; also closes Dependabot alerts #75/#76 |
| #371 | github-actions group, 8 updates | superseded | all 8 included,
same SHAs |
| #352 | `actions/cache` 5.0.5 → 6.1.0 | superseded | included; also
fixes the stale `# v4.3.0` comment it would have left behind |
| #351 | `actions/cache/save` 5.0.5 → 6.1.0 | superseded | included. Its
checks were red (`SBOM + Dependency Scan`, `Security Status`) on the
go-git baseline, which this PR clears |
| #334 | `actions/checkout` 6.0.3 → 7.0.0 | superseded, **improved** |
went to v7.0.1 instead; v7.0.0 predates the `--unset` escaping fix |
| #333 | `alpine` 3.23 → 3.24 | superseded | same digest `28bd5fe8…` |
| #326 | `caddy` digest `f2b9891` → `ea6e54f` | superseded, **improved**
| that digest was already stale (opened Jun 15; upstream rebuilt Jul 7).
Used the current `198d47ea…` / `844f60b6…` instead |

Two Dependabot alerts (#75 HIGH, #76 MEDIUM) are the only open ones and
both close on merge.

## Follow-ups, not done here

- **`reecetech/version-increment` is pinned at `2024.10.1`; `2026.8.4`
is out** (`28d33de033e97a304f77238d6a5db26039ca15d6`). Not bumped on
purpose: no CVE, and the diff touches `version-increment.sh` +
`shared.sh`, the calver logic that decides every release tag in
`push.yaml` and `branch-preview.yaml`. Changing release versioning
inside an SCA PR is the wrong blast radius. Worth its own PR with a
dry-run.
- **`simple-container-com/actions` refs are inconsistent**: 8 pins at
`0af5a697…` and `semgrep.yml` at `5061c0a2…`, both commented `# main`.
Not a vulnerability, but the drift is worth a sweep.

## Evidence

<details><summary>Source scanners, this branch</summary>

```
$ trivy fs --severity CRITICAL,HIGH,MEDIUM,LOW .
main:   HIGH   github.com/go-git/go-git/v5 v5.19.1 -> 5.19.2  CVE-2026-71556
        MEDIUM github.com/go-git/go-git/v5 v5.19.1 -> 5.19.2  CVE-2026-71557
branch: 0 findings

$ govulncheck -mode=source ./...
0 reachable; 1 module-level (GO-2026-5932, VEX not_affected)

$ osv-scanner scan source -L go.mod
main:   2 vulnerabilities (1 HIGH, 1 MEDIUM), 2 fixable
branch: No issues found   (GO-2026-5932 filtered, justified)

$ pip-audit -r docs/requirements.txt
No known vulnerabilities found
```
</details>

<details><summary>Base-image layers, old digest vs new</summary>

```
alpine:3.23 (old)   C=0 H=2  M=8  L=20  total=30
alpine:3.24 (new)   C=0 H=0  M=0  L=0   total=0
caddy:2.11.4 (old)  C=0 H=12 M=23 L=20  total=55
caddy:2.11.4 (new)  C=0 H=10 M=15 L=0   total=25
alpine/kubectl old  C=0 H=20 M=28 L=20  total=68
alpine/kubectl new  C=0 H=5  M=3  L=0   total=8
lambda al2023 old   C=0 H=58 M=5  L=1   total=64
lambda al2023 new   C=0 H=0  M=0  L=0   total=0
                                        217 -> 33
```
</details>

<details><summary>python base image comparison (welder
build-docs)</summary>

```
python:3.9.18     C=248 H=2293 M=5849 L=1720   total=10110   (EOL Oct 2025)
python:3.12       C=60  H=334  M=687  L=1178   total=2259
python:3.12-slim  C=4   H=23   M=86   L=99     total=212     <- chosen
python:3.13-slim  C=4   H=19   M=56   L=66     total=145
```

3.12-slim over 3.13-slim on purpose: `docs/requirements.txt` is
`pip-compile`d for Python 3.12 with `--generate-hashes`, so 3.12 is the
version the lockfile was resolved against. The 67-finding gap is all
LOW/MEDIUM distro noise. Build verified end to end against the real
lockfile:

```
$ docker run --rm -v "$PWD/docs:/w" -w /w python:3.12-slim sh -c \
    'pip install -r requirements.txt && mkdocs build'
INFO - Documentation built in 2.86 seconds
```

(The pre-existing `ai-assistant/examples/*` broken-link warnings are
unchanged and unrelated.) `mkdocs.yml` uses only `search` + `mermaid2`,
no git-dependent plugins, so `-slim` is sufficient.
</details>

## Test plan

- `go mod tidy` clean; `go build ./cmd/github-actions
./cmd/cloud-helpers` succeeds. Our own `dist/github-actions` links
`golang.org/x/text v0.40.0` (patched) per `go version -m`.
- All 5 images build from a clean context on both `origin/main` and this
branch; the before/after tables above come from those builds, not from
published tags.
- `caddy` image smoke-verified post-bump: v2.11.4, alpine 3.23.5, both
`caddy.storage.gcs` and `http.handlers.rate_limit` registered, so the
Dockerfile's build-time module assertion passes.
- `github-actions` image build-time smoke test (`pulumi version`,
`gcloud version`, `gke-gcloud-auth-plugin` present, `aws --version`,
`sc` symlink) passes on alpine 3.24 + gcloud 579.0.0.
- docs build verified in `python:3.12-slim` against the real hashed
lockfile.
- Action SHAs verified against the GitHub git-ref API with annotated
tags dereferenced; no pin was hand-written.

---

# Round 2: multi-model review + preview build

Six independent reviewers (four Claude lenses, OpenAI Codex, Google
Gemini). Every claim below was re-verified before acting on it; two
findings were defects in round 1's own diff, and one review claim turned
out to be a probe artifact.

## Verified in a real pipeline

`branch-preview.yaml` dispatched twice, once per commit. Both **13/13
jobs green**, including the docker builds — the second run with fully
cold caches, since every scope name changed.

| | round 1 (`35cde9a`) | round 2 (`64ddbee`) |
|---|---|---|
| preview version | `v2026.8.4-pre.35cde9a-preview.35cde9a` |
`v2026.8.4-pre.64ddbee-preview.64ddbee` |
| `github-actions` image | C0 **H1** M0 — total 1 | C0 **H1** M0 — total
1 |
| `cloud-helpers` image | — | C0 H0 M0 — **total 0** |

The CI-built image reports `alpine 3.24.1`, `Google Cloud SDK 579.0.0`,
`pulumi v3.245.0`, `gke-gcloud-auth-plugin` present. Against the
published `:latest` from main (128 findings, 2 CRITICAL) that is **128 →
1**, and the one residual is the documented `x/text v0.38.0` inside
Google's auth plugin. The gcloud `SHA256` pin verified inside a real
build, which is the authoritative check on that value.

## Two defects in round 1's own diff

**1. The cache scoping was wrong, not merely inconsistent.** Round 1
scoped per image — but `github-actions` is a matrix value in *both*
`push.yaml` (`matrix.image`) and `branch-preview.yaml`
(`matrix.target`), so they collided on one scope. `branch-preview` is
dispatchable from any branch and `mode=max` exports builder stages, so a
preview build could seed the very layer a release build restores — and a
cache hit means the SHA-256-verified gcloud and Pulumi downloads never
execute. Round 1 separated staging but not preview, which made the gap
easy to miss.

Scopes are now keyed by **trust tier**: releases read and write `prod-*`
only; staging and preview write their own tier and read `prod-*` as a
warm fallback, which is the safe direction (reading higher-trust cannot
poison it). `branch-preview` gained an explicit `cache_scope` so its
fallback matches push.yaml's image naming (`cloud-helpers-aws`, not
`cloud-helpers`) — Gemini and two Claude lenses independently flagged
that mismatch. Expect one cold release build after merge; that is the
scope rename, not a regression.

**2. `welder.yaml` weakened a pin.** Round 1 replaced `python:3.9.18`
with a floating `python:3.12-slim` and a bare `pip install`, against
this repo's own documented digest-pin + `--require-hashes` policy —
while `push.yaml:376` already pinned the same tool by digest. Now
digest-pinned and hash-enforced, identical to the CI docs build, and
**both** refreshed to the current digest (round 1 left push.yaml's three
months stale).

## Pre-existing defects the panel surfaced

**The Pulumi integrity gate could not fail the build.** `... &&
sha256sum -c - && ... && strip … || true && upx … || true` parses as
`((chain || true) && upx) || true`: `&&` and `||` are equal precedence
and left-associative, and errexit does not apply to non-final commands
in an AND-OR list. Verified both ways — with a deliberately wrong
expected hash, the old shape exits **0**, the brace-grouped shape now
**fails**:

```
#8 1.650 sha256sum: WARNING: 1 of 1 computed checksums did NOT match
#8 ERROR: process "/bin/sh -c set -euo pipefail ..." did not complete successfully: exit code: 1
```

The gcloud chain terminates on a real command and was already
fail-closed, so the gcloud claim in this PR was accurate. Fixed in both
github-actions Dockerfiles.

**`simplecontainer/cloud-helpers:aws-latest` cannot start.**
`actions/upload-artifact` does not preserve the executable bit, and
`push.yaml` has no `chmod` for this binary — `branch-preview.yaml:324`
does. The published image has `/cloud-helpers` at `-rw-r--r--` with
`ENTRYPOINT ["/cloud-helpers"]`. Asserted in the Dockerfile rather than
a workflow so it holds whichever path builds it; the CI-built preview
image now reports `-rwxr-xr-x`.

**Docs example images carried the CVEs this PR claims to close.**
`docs/DEPENDENCIES.md` lists example dirs in the tracked Docker surface,
but `dependabot.yml` scanned only the root level and rationalised the
gap in a comment. `blog-platform/caddy.Dockerfile` pinned caddy
**2.11.3** — the exact version whose CVEs this PR fixes at the root —
and six files pinned node **22.22.3** (CVE-2026-48618, CVE-2026-48933,
both HIGH; fixed in 22.23.0). All bumped to digests re-resolved against
the live registry, the docker ecosystem now globs
`docs/docs/examples/**`, and one example moves off EOL `python:3.9`.

## Caddy: version now single-sourced from the base image

Rather than keep three `2.11.x` literals guarded by a comment, `xcaddy`
now builds `"${CADDY_VERSION}"` — the env var the caddy base images set
themselves. There is no second literal to forget, which is precisely
what shipped a 2.11.3 binary on a 2.11.4 base. Both stages assert the
built binary against their own base's version, and `certmagic-gcs` joins
`rate_limit` in the module assertions (a silently dropped storage plugin
means per-pod ACME state and Let's Encrypt lockout).

Worth recording how this landed: my first attempt compared the binary to
a hand-maintained `ARG`, and the negative test showed it passing on a
deliberate skew — self-consistent by construction, proving nothing. My
second negative test was also invalid, because the digest I picked for
"2.11.3" was actually the old 2.11.4 rebuild. Only the third attempt,
against the genuine `caddy:2.11.3` digest, demonstrated the gate firing:

```
ERROR: process "/bin/sh -c test -n \"${CADDY_VERSION}\" && caddy version | grep -qF \"${CADDY_VERSION} \"" did not complete successfully: exit code: 1
```

## VEX corrections

The GO-2026-5932 statement claimed x/crypto was "a direct dependency for
chacha20poly1305 only" while stamped *re-confirmed against the current
tree*. It is not: **24** x/crypto packages link, including `ssh`,
`ssh/agent`, `ssh/knownhosts`, `pkcs12` and `ocsp`, and
`pkg/api/secrets` imports `ssh` directly. The conclusion is unchanged —
openpgp linkage is 0 — but understating the linked surface on the one
artifact an auditor reads is worse than not writing it, and
`x/crypto/ssh` is the subpackage with the CVE history. Two reviewers
caught this independently.

The evidence commands now specify `-buildvcs=false`: a plain `go list
-deps` can exit non-zero on a VCS error and print nothing, which greps
identically to a clean result — the same masking failure mode as `||
true`.

On the aws-sdk-go statements, one reviewer argued
`component_not_present` contradicts an impact_statement that mentions
`go list -m all`, and proposed `vulnerable_code_not_present`. I kept
`component_not_present` and made the scope explicit instead: for the
shipped artifact the component genuinely is absent — 0 packages linked,
**0 `go.sum` entries**, 0 in the built binary's BuildInfo — and the
residual `go list -m all` edge is a requirement-graph reference from a
transitive `go.mod`. `vulnerable_code_not_present` would wrongly imply
the component ships and only the vulnerable function is absent.

## Doc contracts this pass contradicted

`DEPENDENCIES.md` stated that every `not_affected` is mirrored into
`osv-scanner.toml`, and that a superseded one gets flipped to `status:
fixed` — the rule that produced the bug round 1 reverted. Both are now
qualified: the mirror covers only advisories osv-scanner actually
reports (an unused ignore is itself a finding), and an advisory with
`introduced: 0` and no fix event can never reach `fixed`.

## One review claim that did not survive

Codex reported a **different gcloud SHA-256** than the pin. That was its
own probe artifact: it fetched with `curl -C -` (resume) against a
leftover file, producing 97,127,837 bytes and `gzip: trailing garbage
ignored`. A clean download is 96,066,973 bytes hashing to `a9a7fbe5…`,
matching the pin — and CI's own verification passed inside the preview
build. No change needed. Codex otherwise reported clean.

## Deliberately deferred

These are real but wrong to fold into a dependency PR:

- **go-git 5.19.2 hardens worktree paths.** One lens demonstrated that
`AddFileToGit` now rejects a path whose leading component is a symlink
(`pkg/provisioner/common.go` passes `.sc/cfg.yaml.template`), where
5.19.1 accepted it — and that the test suite passes on *both* versions,
so nothing would catch it. The repo has zero `os.Symlink` usage in
tests. Reverting would reintroduce the CVEs, so the fix is a test plus
an actionable error message, and it needs a decision on whether a
symlinked `.sc` is a supported layout. Worth its own issue.
- **`reecetech/version-increment`** at `2024.10.1` (latest `2026.8.4`,
SHA `28d33de0`). Its diff touches the calver logic that names every
release tag.
- **`github-actions-staging.Dockerfile` duplicates ~95 lines** of its
sibling under a "keep in sync" comment; this PR had to edit the alpine
digest and both gcloud ARGs twice.
- **A pin-consistency CI gate** (action SHA vs its `# vX.Y.Z` comment,
gcloud version/SHA parity across the two Dockerfiles). Round 1 found a
`# v4.3.0` comment on a v5.0.5 SHA by hand; a 20-line `gh api` check
would have caught it. `# v4` on the codeql pins is corrected to `#
v4.37.6` here.
- **`DEPENDENCIES.md` claims `trivy image` runs per published image** —
no trivy/grype step exists in any workflow; the shared scan is
source-tree only. That is why the stale caddy binary went unnoticed for
months.
- **No PR-time build of any Dockerfile**; the first build happens
post-merge on `push.yaml`. A `docker build --load` job on a path filter
would close it.
- `certmagic-gcs` v0.1.7→v0.1.8, `golangci-lint` v1→v2, pulumi SDK 11
minors, `mermaid` 10.6.1 in the docs site.

---------

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github-actions Pull requests that update GitHub Actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant