diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0c1b7c3a..65b1f550 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -65,11 +65,17 @@ updates: # ── Docker: VERSION only ──────────────────────────────────────────────── # Production images built from the 5 root *.Dockerfile files (caddy, - # cloud-helpers.aws, github-actions, github-actions-staging, kubectl). - # Dependabot only scans the configured directory level, so the example - # Dockerfiles under docs/ are implicitly out of scope. + # cloud-helpers.aws, github-actions, github-actions-staging, kubectl), + # plus the user-facing example Dockerfiles under docs/ (see below). - package-ecosystem: docker - directory: / + # The user-facing example Dockerfiles under docs/ are part of the tracked + # Docker surface per docs/DEPENDENCIES.md, but a bare `directory: /` only + # scans the root level — so their digests went unwatched and drifted onto + # caddy 2.11.3 and node 22.22.3 (both with HIGH CVEs) until the 2026-08 SCA + # pass caught them by hand. `directories:` with a glob closes that gap. + directories: + - / + - /docs/docs/examples/** schedule: interval: weekly day: monday diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index 22ea4b9a..14fdb9a4 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -31,7 +31,7 @@ jobs: run: | echo "::error title=branch-preview.yaml requires a branch ref::Got github.ref=$GITHUB_REF; this workflow signs preview artifacts under the preview trust root (refs/heads/* only). Re-dispatch from a branch." exit 1 - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Get next version @@ -56,7 +56,7 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 @@ -145,7 +145,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -212,7 +212,7 @@ jobs: - name: SLSA build provenance for sc-${{ matrix.os }}-${{ matrix.arch }} id: slsa_tarball continue-on-error: true - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v${{ needs.prepare.outputs.version }}.tar.gz - name: Materialize SLSA provenance bundle next to tarball @@ -251,7 +251,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -279,7 +279,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -302,17 +302,22 @@ jobs: matrix: include: - target: github-actions + cache_scope: github-actions binary: github-actions dockerfile: github-actions.Dockerfile image_repo: simplecontainer/github-actions tag_prefix: "simplecontainer/github-actions:" - target: cloud-helpers + # matches push.yaml's `image: cloud-helpers-aws` so the prod + # cache fallback above resolves; the artifact/tag names stay + # `cloud-helpers`. + cache_scope: cloud-helpers-aws binary: cloud-helpers dockerfile: cloud-helpers.aws.Dockerfile image_repo: simplecontainer/cloud-helpers tag_prefix: "simplecontainer/cloud-helpers:aws-" steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: download ${{ matrix.target }} binary @@ -331,7 +336,7 @@ jobs: EOF sc secrets reveal - name: Setup Docker Buildx with advanced caching - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 with: driver-opts: | image=moby/buildkit:buildx-stable-1 @@ -342,15 +347,21 @@ jobs: sc stack secret-get -s dist dockerhub-cicd-token | docker login --username simplecontainer --password-stdin - name: Build and push preview ${{ matrix.target }} image id: build_and_push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: ${{ matrix.dockerfile }} platforms: linux/amd64 tags: ${{ matrix.tag_prefix }}${{ needs.prepare.outputs.version }} push: true - cache-from: type=gha - cache-to: type=gha,mode=max + # Writes its own preview tier; reads prod as a warm fallback. + # Reading a higher-trust scope is safe, writing one is not. + # matrix.cache_scope (not matrix.target) so the prod fallback + # actually matches push.yaml's image names. + cache-from: | + type=gha,scope=preview-${{ matrix.cache_scope }} + type=gha,scope=prod-${{ matrix.cache_scope }} + cache-to: type=gha,mode=max,scope=preview-${{ matrix.cache_scope }} provenance: false # Phase 2 attestation (mirrors push.yaml). Preview builds get the SAME # security guarantees as production releases so consumers pin-testing a @@ -410,7 +421,7 @@ jobs: permissions: contents: write # welder deploy reads release artifacts + updates dist steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: download all sc platform artifacts @@ -481,7 +492,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false fetch-depth: 0 @@ -537,7 +548,7 @@ jobs: - publish-git-tag - docker-build steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ always() }} with: persist-credentials: false diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 5aa9dd9e..2e025b37 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -46,7 +46,7 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 @@ -131,7 +131,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -173,7 +173,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -200,7 +200,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: build-setup steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -223,7 +223,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: build-setup steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -256,7 +256,7 @@ jobs: - build-github-actions-staging - test steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ always() }} with: persist-credentials: false diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml index 77d521b1..c8333610 100644 --- a/.github/workflows/build-staging.yml +++ b/.github/workflows/build-staging.yml @@ -37,7 +37,7 @@ jobs: exit 1 - name: Checkout code - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: echo "🏷️ Set VERSION: $VERSION" - name: Cache CLI tools (SC + Welder) - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4.3.0 (Automatically uses Blacksmith cache on Blacksmith runners) + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 (Automatically uses Blacksmith cache on Blacksmith runners) with: path: | ~/.local/bin/sc @@ -90,7 +90,7 @@ jobs: echo "cicd-bot-telegram-chat-id=$(sc stack secret-get -s dist cicd-bot-telegram-chat-id)" >> $GITHUB_OUTPUT - name: Setup Docker Buildx with advanced caching - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 with: driver-opts: | image=moby/buildkit:buildx-stable-1 @@ -106,7 +106,7 @@ jobs: - name: Build and push github-actions staging image id: build_gha_staging - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: github-actions-staging.Dockerfile @@ -115,13 +115,15 @@ jobs: simplecontainer/github-actions:staging simplecontainer/github-actions:${{ github.ref_name }} push: true - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: | + type=gha,scope=staging-github-actions + type=gha,scope=prod-github-actions + cache-to: type=gha,mode=max,scope=staging-github-actions provenance: false - name: Build and push caddy staging image id: build_caddy_staging - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: caddy.Dockerfile @@ -130,8 +132,10 @@ jobs: simplecontainer/caddy:staging simplecontainer/caddy:${{ steps.version.outputs.version }} push: true - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: | + type=gha,scope=staging-caddy + type=gha,scope=prod-caddy + cache-to: type=gha,mode=max,scope=staging-caddy provenance: false # Phase 2: sign + SBOM + SLSA provenance for staging images. Staging has @@ -227,7 +231,7 @@ jobs: needs: - build-staging steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ always() }} with: persist-credentials: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index eadff8e8..8328a53c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -67,13 +67,13 @@ jobs: # directive), which fails the compile and surfaces as a CI red # rather than a real finding (gemini round-1 P2). - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: false - name: Initialize CodeQL - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: languages: ${{ matrix.language }} # security-extended adds queries beyond the default "security @@ -83,9 +83,9 @@ jobs: queries: security-extended - name: Autobuild - uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/autobuild@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 - name: Perform analysis - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4747a96a..32201ece 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -31,12 +31,12 @@ jobs: actions: read # list + download the main baseline artifact steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go (matching go.mod) - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml index 1259557f..8179066d 100644 --- a/.github/workflows/dco.yml +++ b/.github/workflows/dco.yml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout PR commits - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Pull enough history to walk back to the merge base. PRs # against main may sit on top of dozens of base-branch commits. diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index ca7a97e4..f8733206 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -63,12 +63,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod # No build cache here — fuzz needs a clean, reproducible diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index db250033..87147956 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -81,7 +81,7 @@ jobs: GOGC: "25" steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -115,7 +115,7 @@ jobs: - name: Set up Go if: steps.gate.outputs.scan == 'true' - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index cc08221a..a2889de5 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -34,7 +34,7 @@ jobs: run: | echo "::error title=push.yaml restricted to main::Got github.ref=$GITHUB_REF; this workflow signs artifacts under the production trust root (refs/heads/main only). Re-dispatch from main or remove workflow_dispatch from the trigger." exit 1 - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Get next version @@ -53,7 +53,7 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 @@ -118,7 +118,7 @@ jobs: echo "cicd-bot-telegram-token=$(./bin/sc stack secret-get -s dist cicd-bot-telegram-token)" >> $GITHUB_OUTPUT echo "cicd-bot-telegram-chat-id=$(./bin/sc stack secret-get -s dist cicd-bot-telegram-chat-id)" >> $GITHUB_OUTPUT - name: save schemas cache - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: docs/schemas key: schemas-${{ github.run_id }} @@ -147,7 +147,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -228,7 +228,7 @@ jobs: - name: SLSA build provenance for sc-${{ matrix.os }}-${{ matrix.arch }} id: slsa_tarball continue-on-error: true - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: | .sc/stacks/dist/bundle/sc-${{ matrix.os }}-${{ matrix.arch }}-v${{ needs.prepare.outputs.version }}.tar.gz @@ -302,7 +302,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -335,7 +335,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up Go with Blacksmith caching @@ -351,11 +351,11 @@ jobs: runs-on: ubuntu-latest needs: [prepare, build-setup] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: restore cached schemas - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: docs/schemas key: schemas-${{ github.run_id }} @@ -373,7 +373,7 @@ jobs: # base would force keeping the vulnerable transitives. # Digest is multi-platform (OCI image index) — covers amd64 # / arm64 / arm/v5 / arm/v7 / 386 / ppc64le / riscv64 / s390x. - docker run --rm -v $PWD/docs:/docs -w /docs python@sha256:401f6e1a67dad31a1bd78e9ad22d0ee0a3b52154e6bd30e90be696bb6a3d7461 sh -c "pip install --require-hashes -r requirements.txt && PATH=\$PATH:~/.local/bin mkdocs build" + docker run --rm -v $PWD/docs:/docs -w /docs python@sha256:229a2c5bfa27522db7815ea81f9bed70af17ccb9de9fc7ad142b1877b5830d36 sh -c "pip install --require-hashes -r requirements.txt && PATH=\$PATH:~/.local/bin mkdocs build" - name: upload docs artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -424,7 +424,7 @@ jobs: simplecontainer/cloud-helpers:aws-latest simplecontainer/cloud-helpers:aws-${{ needs.prepare.outputs.version }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: download github-actions binary @@ -451,7 +451,7 @@ jobs: EOF sc secrets reveal - name: Setup Docker Buildx with advanced caching - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 with: driver-opts: | image=moby/buildkit:buildx-stable-1 @@ -460,15 +460,20 @@ jobs: sc stack secret-get -s dist dockerhub-cicd-token | docker login --username simplecontainer --password-stdin - name: Build and push ${{ matrix.image }} image id: build_and_push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: ${{ matrix.dockerfile }} platforms: linux/amd64 tags: ${{ matrix.tags }} push: true - cache-from: type=gha - cache-to: type=gha,mode=max + # Cache scopes are prefixed by TRUST TIER, not just by image. + # A lower-trust build must never write a scope a release build + # reads: `mode=max` exports the builder stage, so a poisoned hit + # would skip the SHA-256-verified gcloud/Pulumi downloads entirely. + # Release builds read and write `prod-*` only. + cache-from: type=gha,scope=prod-${{ matrix.image }} + cache-to: type=gha,mode=max,scope=prod-${{ matrix.image }} provenance: false # Phase 2 attestation: keyless cosign sign + CycloneDX SBOM + SLSA L3 # provenance. The publish step above is the gating job. Sign/attest steps @@ -544,7 +549,7 @@ jobs: id-token: write # OIDC for keyless cosign sign-blob of sc.sh attestations: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: fregante/setup-git-user@024bc0b8e177d7e77203b48dab6fb45666854b35 # v2.0.2 @@ -712,7 +717,7 @@ jobs: - build-setup - docker-finalize steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ always() }} with: persist-credentials: false diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 80996f80..47d57b1c 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -41,12 +41,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 with: results_file: results.sarif results_format: sarif @@ -55,7 +55,7 @@ jobs: publish_results: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: sarif_file: results.sarif category: scorecard diff --git a/.github/workflows/simple-forge.yml b/.github/workflows/simple-forge.yml index 20ce8cc6..688dc9c6 100644 --- a/.github/workflows/simple-forge.yml +++ b/.github/workflows/simple-forge.yml @@ -50,7 +50,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/verify-attestations.yml b/.github/workflows/verify-attestations.yml index f6c3b9f6..4914296a 100644 --- a/.github/workflows/verify-attestations.yml +++ b/.github/workflows/verify-attestations.yml @@ -64,7 +64,7 @@ jobs: STAGING_IDENTITY_REGEX: '^https://github\.com/simple-container-com/api/\.github/workflows/build-staging\.yml@refs/heads/staging$' PREVIEW_IDENTITY_REGEX: '^https://github\.com/simple-container-com/api/\.github/workflows/branch-preview\.yaml@refs/heads/.+$' steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/caddy.Dockerfile b/caddy.Dockerfile index 0daf6870..2af6e1cc 100644 --- a/caddy.Dockerfile +++ b/caddy.Dockerfile @@ -1,9 +1,21 @@ -# Caddy 2.11.3: closes vendored-dep CVEs in 2.11.2's binary (go-jose v4, -# otel, smallstep/certificates) plus Caddy core fastcgi + admin-socket -# auth-bypass fixes — see https://github.com/caddyserver/caddy/releases/tag/v2.11.3. -# Bumping requires editing all three "2.11.x" sites below (two FROMs + xcaddy). +# Caddy 2.11.4: closes CVE-2026-52844, CVE-2026-52845 and CVE-2026-52846 on +# top of 2.11.3's vendored-dep CVEs (go-jose v4, otel, smallstep/certificates) +# and core fastcgi + admin-socket auth-bypass fixes. +# +# The version lives in ONE place: the CADDY_VERSION ARG below. It feeds both +# FROMs and `xcaddy build`, because `COPY --from=builder /usr/bin/caddy` +# overwrites the runtime image's own binary — so a builder/runtime version skew +# ships silently. Only the two digests are per-tag and must be refreshed with it. # Refresh: docker buildx imagetools inspect caddy:X.Y.Z[-builder] # +# NOTE: 2.11.4 is a security release upstream flags as breaking if you relied on +# the buggy behaviour — request header fields containing underscores are now +# ignored, Windows backslashes are normalised in the path matcher, and `rewrite` +# no longer re-expands placeholders in an injected query. SC's own generated +# Caddyfiles use none of those, but consumers injecting headers via +# lbConfig.extraHelpers / siteExtraHelpers should check for underscore-named +# request headers. +# # Plugins: # - github.com/grafana/certmagic-gcs — GCS-backed certmagic storage for GKE. # - github.com/mholt/caddy-ratelimit — request rate limiting (third-party @@ -57,26 +69,41 @@ # Verify the LB is `externalTrafficPolicy: Local` + the parent # Caddy's `trustedProxies` covers the LB CIDR range. -FROM caddy:2.11.4-builder@sha256:f2b98918658f949a3c533f2c73bd0806e3f2576ccf8eb182c8b1690c977007ea AS builder +FROM caddy:2.11.4-builder@sha256:198d47eaee306d4d0c38a9960c89ff2c959aa29ad51d3e2dafa3e93ac961782a AS builder +# `$CADDY_VERSION` is set by the base image itself (v2.11.4 here), so xcaddy +# builds exactly the version the builder ships and a skew is impossible by +# construction — there is no second version literal to forget. The tag on the +# FROM line is informational only; the digest is what resolves. RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ --mount=type=cache,target=/root/.cache,sharing=locked \ - xcaddy build "v2.11.3" \ + test -n "${CADDY_VERSION}" \ + && xcaddy build "${CADDY_VERSION}" \ --with github.com/grafana/certmagic-gcs@v0.1.7 \ --with github.com/mholt/caddy-ratelimit@16aecbbcb8ca07dc1c671e263379606ff9493c55 \ - && caddy version \ - && caddy list-modules | grep -qE '^http\.handlers\.rate_limit$' -# ^ Final grep is a sanity check that the ratelimit module actually registered -# into the resulting binary (xcaddy has been known to silently drop plugins -# when versions disagree). If this fails the RUN exits non-zero with the -# failing command visible — no misleading prefixed echo. + && caddy version | grep -qF "${CADDY_VERSION} " \ + && caddy list-modules | grep -qE '^http\.handlers\.rate_limit$' \ + && caddy list-modules | grep -qE '^caddy\.storage\.gcs$' +# ^ The greps are gates, not decoration: +# - `caddy version | grep` pins the built binary to the base image's own +# version. The previous line printed `caddy version` and never compared it, +# which is how a 2.11.3 binary shipped inside a 2.11.4 base unnoticed. +# - both module greps catch a silently dropped plugin (xcaddy does this when +# versions disagree). Dropping certmagic-gcs is the expensive one: Caddy +# falls back to local-filesystem cert storage, so a multi-replica parent +# stack gets per-pod ACME state and risks Let's Encrypt rate-limit lockout. -FROM caddy:2.11.4@sha256:cb9d71ad83182011b79355cd57692686374bd78d6fe327efe0ff8507da03ab13 +FROM caddy:2.11.4@sha256:844f60b64e4724a5aa8245e019dace0d3f199f7433ce6c57676cb30a920dbad9 RUN apk update && apk upgrade --no-cache && rm -rf /var/cache/apk/* COPY --from=builder /usr/bin/caddy /usr/bin/caddy +# Re-assert against the RUNTIME base's own $CADDY_VERSION: this is what catches +# builder/runtime digest skew, and stops a cache-hit builder stage slipping a +# stale binary into a freshly-pulled runtime base. +RUN test -n "${CADDY_VERSION}" && caddy version | grep -qF "${CADDY_VERSION} " + LABEL org.opencontainers.image.source="https://github.com/simple-container-com/api" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.title="simplecontainer/caddy" \ diff --git a/cloud-helpers.aws.Dockerfile b/cloud-helpers.aws.Dockerfile index 55b70583..9c1998ca 100644 --- a/cloud-helpers.aws.Dockerfile +++ b/cloud-helpers.aws.Dockerfile @@ -1,5 +1,5 @@ # Refresh: docker buildx imagetools inspect public.ecr.aws/lambda/provided:al2023 -FROM public.ecr.aws/lambda/provided:al2023@sha256:777e461e02cd42bb0fdc692c0e8b056ed612f103910ecf8c5463d6fd7a92cde2 +FROM public.ecr.aws/lambda/provided:al2023@sha256:5f3ae3216e07bb3677cc4dfa0c7867973f7e536abb114d6b44a7b8c558824812 # Pull post-tag distro fixes (e.g. glibc CVE-2026-4046 once published to AL2023 dnf). RUN dnf upgrade -y --setopt=tsflags=nodocs \ @@ -8,6 +8,11 @@ RUN dnf upgrade -y --setopt=tsflags=nodocs \ WORKDIR / COPY dist/cloud-helpers /cloud-helpers +# actions/upload-artifact does not preserve the executable bit, and the release +# path (push.yaml) downloads this binary without a chmod — unlike +# branch-preview.yaml, which has one. Assert it here so the image is correct +# regardless of which workflow built it. +RUN chmod +x /cloud-helpers && test -x /cloud-helpers EXPOSE 8080 LABEL org.opencontainers.image.source="https://github.com/simple-container-com/api" \ diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index f069381a..36567858 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -27,7 +27,7 @@ selects, obtains, and tracks its dependencies." | **Go** | `go.mod` + `go.sum` | `go.sum` hashes every direct + transitive dep | `govulncheck` (reachability-aware), `osv-scanner` (via Scorecard), `trivy fs` | | **Python (docs)** | `docs/requirements.in` (sources) + `docs/requirements.txt` (compiled with `--generate-hashes`) | `pip install --require-hashes` in `push.yaml` docs-build step | `pip-audit`, Scorecard pinned-deps check | | **npm (docs examples)** | example `package*.json` files | Lockfile-aware install (`npm ci` when lockfile present, falls back to `npm install`) | Scorecard pinned-deps check | -| **Docker base images** | `Dockerfile`s + `.Dockerfile`s at repo root + example dirs | SHA digest pin: `python@sha256:401f...`, `node:22-alpine@sha256:757e...` | `trivy image` per published image | +| **Docker base images** | `Dockerfile`s + `.Dockerfile`s at repo root + example dirs | SHA digest pin (digests live in the files, not here — grep `@sha256:`) | `trivy image` per published image | | **GitHub Actions** | `.github/workflows/*` + `.github/actions/*` | Commit SHA pin with `# vX.Y.Z` comment for human-readability | Scorecard pinned-deps check; Semgrep custom rules | | **End-user installer tools** | `sc.sh` (Pulumi installer) | Tarball + SHA256 checksum verification before extract | n/a (sc.sh is shipped, not built against) | @@ -95,8 +95,9 @@ positives — those live in PR descriptions and as OpenVEX `not_affected` statements in [`vex/openvex.json`](../vex/openvex.json), never in a scanner-suppression file (no `.trivyignore`, no `# nosemgrep`, no `// nolint:` for vuln findings). The single exception -is [`osv-scanner.toml`](../osv-scanner.toml), a derivative mirror of the -VEX statements for OpenSSF Scorecard, which cannot read VEX — see +is [`osv-scanner.toml`](../osv-scanner.toml), a derivative partial +mirror (only advisories osv-scanner actually reports) of the VEX +statements for OpenSSF Scorecard, which cannot read VEX — see [Suppressing a finding](#suppressing-a-finding-non-exploitable--false-positive). ## Out-of-tree dependency surface @@ -184,9 +185,14 @@ OpenSSF Scorecard's Vulnerabilities check calls `osv-scanner` directly and has no VEX input, so a VEX-only `not_affected` still shows up as a score deduction. For that one consumer the statement is mirrored into [`osv-scanner.toml`](../osv-scanner.toml) — same advisory ID, the -`reason` field restating the VEX justification. That mirror is the only -scanner-config suppression the project sanctions, and it is derivative: -an entry may exist there **only** when the VEX statement exists first. +`reason` field restating the VEX justification — but **only when +osv-scanner actually reports the advisory**. osv-scanner reads go.mod's +own requirements, so an advisory against a module that appears only in +the wider module graph (`go list -m all`) needs no mirror entry; adding +one anyway produces an `unused ignores` warning, which is itself a +finding. That mirror is the only scanner-config suppression the project +sanctions, and it is derivative: an entry may exist there **only** when +the VEX statement exists first. `/vex/` and `/osv-scanner.toml` are both code-owned paths so neither lands without security review. @@ -196,6 +202,15 @@ to `status: fixed`, and dropped from the mirror — a suppression that outlives its justification is not triage. `osv-scanner` reports stale entries as `unused ignores`, which is the signal to remove them. +The inverse also has to be checked: an advisory with `introduced: 0` and +**no fix event** in OSV can never reach `status: fixed`, because no +version of the dependency is patched (AWS's aws-sdk-go v1 `s3crypto` +advisories and the `x/crypto/openpgp` unmaintained notice are both this +shape). Writing `fixed` there produces a permanent suppression wearing a +transitional label. Such an advisory stays `not_affected` with the +narrowest true justification, and its closing condition is the +dependency edge disappearing, not a version bump. + `.trivyignore`, `# nosemgrep`, `// nolint:`, `# noqa` are NOT sanctioned suppression channels. Any of these in a PR must point at a documented false positive in the PR description; the project diff --git a/docs/docs/examples/advanced-configs/high-resource/Dockerfile b/docs/docs/examples/advanced-configs/high-resource/Dockerfile index d01f8162..23506a37 100644 --- a/docs/docs/examples/advanced-configs/high-resource/Dockerfile +++ b/docs/docs/examples/advanced-configs/high-resource/Dockerfile @@ -1,5 +1,5 @@ # AI Development Environment Dockerfile -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 # Install system dependencies RUN apk add --no-cache \ diff --git a/docs/docs/examples/advanced-configs/index.md b/docs/docs/examples/advanced-configs/index.md index 9e295bce..6c1273ca 100644 --- a/docs/docs/examples/advanced-configs/index.md +++ b/docs/docs/examples/advanced-configs/index.md @@ -57,7 +57,7 @@ services: - training-logs:/logs mlflow: - image: python:3.9 + image: python:3.12-slim ports: - "5000:5000" command: | diff --git a/docs/docs/examples/ecs-deployments/backend-service/Dockerfile b/docs/docs/examples/ecs-deployments/backend-service/Dockerfile index 7b5603ed..eaceb51d 100644 --- a/docs/docs/examples/ecs-deployments/backend-service/Dockerfile +++ b/docs/docs/examples/ecs-deployments/backend-service/Dockerfile @@ -1,5 +1,5 @@ # Node.js backend service Dockerfile -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 # Install dumb-init for proper signal handling RUN apk add --no-cache dumb-init curl diff --git a/docs/docs/examples/ecs-deployments/blockchain-service/Dockerfile b/docs/docs/examples/ecs-deployments/blockchain-service/Dockerfile index d887169b..cdab18ad 100644 --- a/docs/docs/examples/ecs-deployments/blockchain-service/Dockerfile +++ b/docs/docs/examples/ecs-deployments/blockchain-service/Dockerfile @@ -1,5 +1,5 @@ # Node.js blockchain service Dockerfile -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 # Install dumb-init for proper signal handling RUN apk add --no-cache dumb-init curl diff --git a/docs/docs/examples/ecs-deployments/blog-platform/caddy.Dockerfile b/docs/docs/examples/ecs-deployments/blog-platform/caddy.Dockerfile index 81f8ee7a..c7b3c362 100644 --- a/docs/docs/examples/ecs-deployments/blog-platform/caddy.Dockerfile +++ b/docs/docs/examples/ecs-deployments/blog-platform/caddy.Dockerfile @@ -1,4 +1,4 @@ -FROM caddy:2.11-alpine@sha256:3739ea4f0c877259a693d932693cf8f3408e9a9497c004f031b0e830e93e1546 +FROM caddy:2.11.4-alpine@sha256:5f5c8640aae01df9654968d946d8f1a56c497f1dd5c5cda4cf95ab7c14d58648 # Copy custom Caddyfile COPY Caddyfile /etc/caddy/Caddyfile diff --git a/docs/docs/examples/ecs-deployments/meteor-app/README.md b/docs/docs/examples/ecs-deployments/meteor-app/README.md index 195ae8af..1ee02a54 100644 --- a/docs/docs/examples/ecs-deployments/meteor-app/README.md +++ b/docs/docs/examples/ecs-deployments/meteor-app/README.md @@ -70,7 +70,7 @@ move to a newer Node 22 patch release. ```dockerfile # Multi-stage build for Meteor.js application -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c AS builder +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 AS builder # Install Meteor CLI at a pinned version. Bump as needed for your app; # do not leave unpinned. @@ -98,7 +98,7 @@ RUN if [ -f package-lock.json ]; then meteor npm ci; else meteor npm install; fi RUN meteor build --directory /app/build --architecture os.linux.x86_64 # Production stage -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 # Install dumb-init for proper signal handling RUN apk add --no-cache dumb-init diff --git a/docs/docs/examples/kubernetes-native/streaming-platform/Dockerfile b/docs/docs/examples/kubernetes-native/streaming-platform/Dockerfile index e7830276..9a7728da 100644 --- a/docs/docs/examples/kubernetes-native/streaming-platform/Dockerfile +++ b/docs/docs/examples/kubernetes-native/streaming-platform/Dockerfile @@ -1,5 +1,5 @@ # Node.js streaming platform Dockerfile -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 # Install dumb-init for proper signal handling RUN apk add --no-cache dumb-init curl diff --git a/docs/docs/examples/kubernetes-vpa/Dockerfile b/docs/docs/examples/kubernetes-vpa/Dockerfile index 100ec890..5f64840d 100644 --- a/docs/docs/examples/kubernetes-vpa/Dockerfile +++ b/docs/docs/examples/kubernetes-vpa/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22-alpine@sha256:757ec364de4d37cedf30871be2988927660834e656e9aa52aad9ac194814c30c +FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32 WORKDIR /app diff --git a/github-actions-staging.Dockerfile b/github-actions-staging.Dockerfile index 49d11746..0cb12c2e 100644 --- a/github-actions-staging.Dockerfile +++ b/github-actions-staging.Dockerfile @@ -2,7 +2,7 @@ # difference is that it consumes ./bin/github-actions (built by welder) instead # of dist/github-actions (built by CI). Keep the two files in sync. -FROM alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 AS builder +FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b AS builder RUN apk update && apk upgrade --no-cache \ && apk add --no-cache curl bash binutils upx ca-certificates tar python3 \ @@ -27,11 +27,11 @@ RUN --mount=type=cache,target=/tmp/pulumi-dl,sharing=locked \ && tar -xzf "${TARBALL}" -C /tmp \ && mv /tmp/pulumi/* /opt/pulumi/bin/ \ && rm -rf /tmp/pulumi /tmp/go.mod \ - && strip /opt/pulumi/bin/* 2>/dev/null || true \ - && upx --best --lzma /opt/pulumi/bin/* 2>/dev/null || true + && { strip /opt/pulumi/bin/* 2>/dev/null || true; } \ + && { upx --best --lzma /opt/pulumi/bin/* 2>/dev/null || true; } -ARG GCLOUD_VERSION="567.0.0" -ARG GCLOUD_SHA256="bd5afc0d249609cb40d45f665209190fdd38b9937954291b8f9ae54206c75d83" +ARG GCLOUD_VERSION="579.0.0" +ARG GCLOUD_SHA256="a9a7fbe51cda37cf6142b1bbcff12227550e60a6c67e8cf84644fb301371c4de" RUN --mount=type=cache,target=/tmp/gcloud-dl,sharing=locked \ set -euo pipefail \ && TARBALL="google-cloud-cli-${GCLOUD_VERSION}-linux-x86_64.tar.gz" \ @@ -73,7 +73,7 @@ RUN rm -rf \ && rm -rf /tmp/* /var/tmp/* # ── runtime ───────────────────────────────────────────────────────────────── -FROM alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 +FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b # aws-cli needed by Pulumi local.Command shell-outs (e.g. `aws s3 sync` in the # static-website template at pkg/clouds/pulumi/aws/static_website.go). diff --git a/github-actions.Dockerfile b/github-actions.Dockerfile index 2863e9b6..b1e328d4 100644 --- a/github-actions.Dockerfile +++ b/github-actions.Dockerfile @@ -4,8 +4,8 @@ # USER stays root: GitHub mounts /github/workspace as root, non-root breaks # git ops. HEALTHCHECK omitted: one-shot action, never long-running. -# Refresh: docker buildx imagetools inspect alpine:3.21 -FROM alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 AS builder +# Refresh: docker buildx imagetools inspect alpine:3.24 +FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b AS builder # python3 needed so `gcloud components install` doesn't fall back to (and recreate) the bundled Python we want to delete. RUN apk update && apk upgrade --no-cache \ @@ -34,13 +34,13 @@ RUN --mount=type=cache,target=/tmp/pulumi-dl,sharing=locked \ && tar -xzf "${TARBALL}" -C /tmp \ && mv /tmp/pulumi/* /opt/pulumi/bin/ \ && rm -rf /tmp/pulumi /tmp/go.mod \ - && strip /opt/pulumi/bin/* 2>/dev/null || true \ - && upx --best --lzma /opt/pulumi/bin/* 2>/dev/null || true + && { strip /opt/pulumi/bin/* 2>/dev/null || true; } \ + && { upx --best --lzma /opt/pulumi/bin/* 2>/dev/null || true; } # gcloud: pinned version + SHA-256 (Google does not publish per-release sig). # Refresh: pull the tarball, sha256sum it, paste below. -ARG GCLOUD_VERSION="567.0.0" -ARG GCLOUD_SHA256="bd5afc0d249609cb40d45f665209190fdd38b9937954291b8f9ae54206c75d83" +ARG GCLOUD_VERSION="579.0.0" +ARG GCLOUD_SHA256="a9a7fbe51cda37cf6142b1bbcff12227550e60a6c67e8cf84644fb301371c4de" RUN --mount=type=cache,target=/tmp/gcloud-dl,sharing=locked \ set -euo pipefail \ && TARBALL="google-cloud-cli-${GCLOUD_VERSION}-linux-x86_64.tar.gz" \ @@ -84,7 +84,7 @@ RUN rm -rf \ && rm -rf /tmp/* /var/tmp/* # ── runtime ───────────────────────────────────────────────────────────────── -FROM alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 +FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b # python3 stays — gcloud invokes it. py3-pip / binutils / upx confined to builder. # aws-cli needed by Pulumi local.Command shell-outs (e.g. `aws s3 sync` in the diff --git a/go.mod b/go.mod index b219fbb8..02687d27 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/fatih/color v1.19.0 github.com/go-delve/delve v1.27.0 github.com/go-git/go-billy/v5 v5.9.0 - github.com/go-git/go-git/v5 v5.19.1 + github.com/go-git/go-git/v5 v5.19.2 github.com/golangci/golangci-lint v1.64.8 github.com/google/uuid v1.6.0 github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef diff --git a/go.sum b/go.sum index 5e02fdef..9b20dd97 100644 --- a/go.sum +++ b/go.sum @@ -370,8 +370,8 @@ github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmm github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= -github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= +github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY= +github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= diff --git a/kubectl.Dockerfile b/kubectl.Dockerfile index 7a0f5547..2125d88a 100644 --- a/kubectl.Dockerfile +++ b/kubectl.Dockerfile @@ -1,5 +1,5 @@ # Refresh: docker buildx imagetools inspect alpine/kubectl:latest -FROM alpine/kubectl:latest@sha256:405e7130cb2cd07f85feafd11333f2f46e921f2f8937c375e396791f4776c948 +FROM alpine/kubectl:latest@sha256:5d380d18d2509483aef3df54d676c767d798d55ec9f3e02dabfa4c88fe6559bd # apk upgrade pulls post-tag distro fixes (e.g. nghttp2 CVE-2026-27135 at scan time). RUN apk update \ diff --git a/osv-scanner.toml b/osv-scanner.toml index 39d0339b..68ea25bf 100644 --- a/osv-scanner.toml +++ b/osv-scanner.toml @@ -1,8 +1,13 @@ -# OSV-Scanner ignore list. Mirrors the `not_affected` statements in -# `vex/openvex.json` for the benefit of OpenSSF Scorecard's Vulnerabilities -# check, which queries osv.dev directly and doesn't read VEX. The VEX file -# remains the source-of-truth justification; this file is a re-statement -# in a format the scanner understands. +# OSV-Scanner ignore list. Mirrors the SUBSET of `vex/openvex.json` +# `not_affected` statements that osv-scanner actually reports, for the benefit +# of OpenSSF Scorecard's Vulnerabilities check, which queries osv.dev directly +# and doesn't read VEX. The VEX file remains the source-of-truth justification; +# this file is a re-statement in a format the scanner understands. +# +# A VEX statement with no corresponding osv-scanner finding gets NO entry here +# (currently 1 of 3 statements is mirrored) — osv-scanner reads go.mod's own +# requirements, so an advisory against a module that only appears in the wider +# module graph would be flagged as an `unused ignore`. # # Add an entry here ONLY after the corresponding `not_affected` statement # lands in vex/openvex.json (both this file and /vex/ are named @@ -12,12 +17,17 @@ # # Reference: https://google.github.io/osv-scanner/configuration/ # -# GO-2022-0635 / GO-2022-0646 (aws-sdk-go v1 s3crypto) were dropped from -# this file on 2026-08-02: the module left go.mod entirely through Go -# module pruning after the Pulumi upgrades, osv-scanner reported both -# entries as unused ignores, and their VEX statements are now -# `status: fixed`. Entries here are re-triaged on every SCA pass — an -# ignore that outlives its justification is a suppression, not triage. +# GO-2022-0635 / GO-2022-0646 (aws-sdk-go v1 s3crypto) stay OUT of this +# file. They were dropped on 2026-08-02 because osv-scanner flagged them as +# unused ignores, and that still holds: osv-scanner reads go.mod's own +# requirements, and aws-sdk-go v1 is not one of them. It does still show up +# in the wider module graph (`go list -m all` → v1.55.8) via a transitive +# go.mod, so the VEX statements are kept — but as `not_affected` +# (component_not_present, zero packages linked), NOT `fixed`: both advisories +# carry `introduced: 0` with no fix event, so `fixed` was never reachable. +# Corrected 2026-08-10. Re-add here only if osv-scanner starts reporting them. +# Entries here are re-triaged on every SCA pass — an ignore that outlives its +# justification is a suppression, not triage. # # NOTE: `reason` must stay a single-line TOML basic string. A newline in # it makes osv-scanner discard the WHOLE config ("strings cannot contain @@ -25,4 +35,4 @@ [[IgnoredVulns]] id = "GO-2026-5932" -reason = "VEX not_affected (vulnerable_code_not_present): golang.org/x/crypto is required for chacha20poly1305 (pkg/api/secrets/ciphers); nothing in the build graph imports golang.org/x/crypto/openpgp or its subpackages, so the linker never emits that code. OpenPGP handling uses github.com/ProtonMail/go-crypto/openpgp — the maintained fork this very advisory recommends. govulncheck -mode=source confirms 0 reachable vulnerabilities. Unfixable by bumping: the advisory has introduced=0 and no fix event; it is a permanent 'unmaintained package' notice. Full justification in vex/openvex.json." +reason = "VEX not_affected (vulnerable_code_not_present): golang.org/x/crypto is a direct dependency for chacha20poly1305, ed25519, hkdf and ssh (pkg/api/secrets, pkg/api/secrets/ciphers), and 24 of its packages link into shipped binaries — but none of them is openpgp or an openpgp subpackage, so the linker never emits that code. Verify with `go list -buildvcs=false -deps ./... | grep -c '^golang.org/x/crypto/openpgp'` (expect 0; -buildvcs=false matters because a plain go list can exit non-zero and print nothing, which greps identically to a clean result). OpenPGP handling uses github.com/ProtonMail/go-crypto v1.1.6 — the maintained fork this very advisory recommends. govulncheck -mode=source confirms 0 reachable vulnerabilities. Unfixable by bumping: the advisory has introduced=0 and no fix event; it is a permanent 'unmaintained package' notice. Full justification in vex/openvex.json." diff --git a/vex/openvex.json b/vex/openvex.json index f12f2416..098f155c 100644 --- a/vex/openvex.json +++ b/vex/openvex.json @@ -2,9 +2,9 @@ "@context": "https://openvex.dev/ns/v0.2.0", "@id": "https://simple-container.com/.well-known/openvex/api.json", "author": "Simple Container maintainers ", - "timestamp": "2026-08-02T12:00:00Z", - "version": 2, - "tooling": "Hand-authored. Reachability evidence from `govulncheck -mode=source ./...`.", + "timestamp": "2026-08-10T00:00:00Z", + "version": 3, + "tooling": "Hand-authored. Reachability evidence from `govulncheck -mode=source ./...`, `go list -deps ./...` and `go mod why -m`.", "statements": [ { "vulnerability": { @@ -21,8 +21,9 @@ ] } ], - "status": "fixed", - "impact_statement": "Re-triaged 2026-08-02: github.com/aws/aws-sdk-go (v1) no longer appears in go.mod at all — the Pulumi upgrades since PR #310 dropped the last build-graph edge that pulled it in, so Go 1.17+ module pruning removed it from the module requirements. osv-scanner no longer reports this advisory against the repo and flags the corresponding osv-scanner.toml entry as an unused ignore; that entry has been removed. Statement kept one release as an audit trail." + "status": "not_affected", + "justification": "component_not_present", + "impact_statement": "Corrected 2026-08-10 (was wrongly `fixed`). The advisory has `introduced: 0` and NO fix event in OSV: AWS never patched the v1 s3crypto client, the remediation is migrating to the V2 encryption client. `fixed` was therefore never a reachable status and was acting as an indefinite suppression. Scope of `component_not_present`: the SHIPPED ARTIFACT. aws-sdk-go is absent from go.mod (0 matches) and from go.sum (0 matches), and ZERO of its packages link into any binary we ship — `go list -buildvcs=false -deps ./... | grep -c '^github.com/aws/aws-sdk-go/'` returns 0, `go version -m dist/github-actions | grep -c aws-sdk-go` returns 0, and `go mod why -m github.com/aws/aws-sdk-go` reports \"main module does not need module github.com/aws/aws-sdk-go\". The only residual trace is a requirement-graph edge: `go list -m all` still resolves v1.55.8 because a transitive dependency's own go.mod names it. s3crypto specifically is never imported; S3 access goes through aws-sdk-go-v2. Neither govulncheck -mode=source nor osv-scanner reports this advisory against the repo (osv-scanner reads go.mod's own requirements), so an osv-scanner.toml mirror entry would be an unused ignore. Closing condition: the requirement edge disappears from `go list -m all`, at which point this statement can be deleted." }, { "vulnerability": { @@ -39,8 +40,9 @@ ] } ], - "status": "fixed", - "impact_statement": "Same as GO-2022-0635 — the aws-sdk-go v1 module is no longer part of the build graph. Re-triaged 2026-08-02." + "status": "not_affected", + "justification": "component_not_present", + "impact_statement": "Same module, same evidence and same correction as GO-2022-0635 — see that statement. Also `introduced: 0` with no fix event, so it was likewise mis-stated as `fixed` before 2026-08-10." }, { "vulnerability": { @@ -58,7 +60,7 @@ ], "status": "not_affected", "justification": "vulnerable_code_not_present", - "impact_statement": "golang.org/x/crypto is a direct dependency for chacha20poly1305 only (pkg/api/secrets/ciphers). No package in the build graph imports golang.org/x/crypto/openpgp or any of its subpackages — `go list -deps ./... | grep x/crypto/openpgp` returns nothing, so the Go linker never includes that code in any produced binary. OpenPGP operations in this codebase go through github.com/ProtonMail/go-crypto/openpgp, which is the maintained fork the advisory itself recommends. govulncheck -mode=source ./... reports 0 reachable vulnerabilities. This advisory can never be closed by a version bump: it is a permanent 'package is unmaintained' notice with `introduced: 0` and no fix event in OSV. x/crypto is kept current regardless (v0.54.0)." + "impact_statement": "Re-confirmed 2026-08-10 against the current tree. golang.org/x/crypto is a direct dependency for chacha20poly1305, ed25519, hkdf and ssh (pkg/api/secrets, pkg/api/secrets/ciphers); 24 of its packages link into shipped binaries, including ssh, ssh/agent, ssh/knownhosts, pkcs12 and ocsp. None of them is openpgp or an openpgp subpackage: `go list -buildvcs=false -deps ./... | grep -c '^golang.org/x/crypto/openpgp'` returns 0, so the Go linker never includes that code in any produced binary. (Use -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.) OpenPGP operations go through github.com/ProtonMail/go-crypto v1.1.6, the maintained fork the advisory itself recommends. govulncheck -mode=source ./... reports 0 reachable vulnerabilities. Unfixable by bumping: the advisory has `introduced: 0` and no fix event; it is a permanent 'package is unmaintained' notice. x/crypto is kept current regardless — pinned at v0.54.0, which is the latest release." } ] } diff --git a/welder.yaml b/welder.yaml index c0023016..e3490bda 100644 --- a/welder.yaml +++ b/welder.yaml @@ -275,10 +275,14 @@ tasks: - git tag ${project:version} || echo "Already set tag ${project:version}" - git push -f origin ${project:version} || echo "Tag ${project:version} already exists in origin" build-docs: - image: python:3.9.18 + # Same digest + --require-hashes as the CI docs build in + # .github/workflows/push.yaml, so the two definitions cannot drift. + # python@sha256:401f6e1a = 3.12.13-slim-trixie; docs/requirements.txt is + # pip-compiled for 3.12. + image: python@sha256:229a2c5bfa27522db7815ea81f9bed70af17ccb9de9fc7ad142b1877b5830d36 workDir: ${project:root}/docs script: - - pip install -r requirements.txt + - pip install --require-hashes -r requirements.txt - PATH=${PATH}:~/.local/bin mkdocs build debug-aws-cloud-helpers: runOn: host