normalize public keys - #13
Merged
Merged
Conversation
Laboratory
approved these changes
Oct 7, 2025
Cre-eD
added a commit
that referenced
this pull request
May 17, 2026
…ools composites simple-container-com/actions PR #12 + #13 merged on 2026-05-17, exposing two consolidated composites the api repo can now reach via `@<sha>`. Migrate the matching call sites here: 1. Five `install-sc + install-welder` PAIRS collapse to a single `setup-sc-tooling` invocation. Saves five `- name: ...` blocks / ten `uses:` lines. Pairs touched: - push.yaml (×2) - branch-preview.yaml - build-staging.yml - branch.yaml Standalone `install-sc` (branch-preview ×3, push ×1) and the `install-welder`-only call in simple-forge.yml stay on the individual composites — the new wrapper does not improve them and consistency is not worth a contracting diff. The standalone composites remain part of the shared ruleset. 2. Six api-local `./.github/actions/install-attest-tools` refs swap to the now-shared `simple-container-com/actions/install-attest-tools@<sha>`. Same pinned cosign / syft versions (v2.4.1 / v1.16.0) — drop-in replacement. The api-local copy at `.github/actions/install-attest-tools/action.yml` is removed. Net diff: -26 LOC, no behavior change at any call site (composite contracts are identical to the inlined call sequences they replace). actionlint warning count unchanged vs baseline. Step C-1 (`sign-and-attest`) is NOT adopted in this commit — the inline signing blocks in push.yaml / branch-preview.yaml / build-staging.yml are functionally identical to the composite, but the swap deserves its own preview-build validation cycle, and that cycle fits better in the Step C-2 session that also lands the wrapping `sc-image-release.yml` reusable workflow. Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
4 tasks
Cre-eD
added a commit
that referenced
this pull request
May 17, 2026
simple-container-com/actions PR #13 merged on 2026-05-17, exposing the `sign-and-attest` composite that encapsulates the four-step attestation flow (CycloneDX SBOM via syft + cosign keyless sign + cosign attest cyclonedx + SLSA L3 via actions/attest-build-provenance). The composite has its own end-to-end self-test on the actions repo (`self-test-sign-and-attest.yml` — actually pushes an image to ghcr.io and runs all three verification commands), so the contract is validated against real Sigstore on every actions-repo PR. Migrate the matching call sites here. Each migration is a 5-step → 1-step swap: install-attest-tools + Generate SBOM + Cosign sign + Cosign attest SBOM + SLSA build provenance all collapse into one `sign-and-attest` invocation with `soft-fail: 'true'` (matches the prior continue-on-error default during Phase 2 bake-in). Files touched: - push.yaml (docker-build matrix): 4 images × 5 steps → 1 step - branch-preview.yaml (image-build matrix): same - build-staging.yml: 2 images × 4-or-5 steps → 2 steps. Kept the top-level `install-attest-tools` step here because both composite invocations share the runner; the two `sign-and-attest` calls use `install-tools: 'false'` so the install is paid once. Aggregator steps that emit ::warning:: rewrite from per-step `steps.X.outcome` to the composite's output values (`steps.signattest.outputs.{sbom,sign,attest,slsa}-outcome`). Same semantics: warn if any sub-step is not `success` (or `skipped` for the build-staging case where one of two images may not have been built). Net diff: −129 LOC across 3 workflows. Behaviour is byte-identical to the inline blocks the composite replaces — the composite's implementation is the exact same 4-step sequence the inline blocks performed, validated by the actions-repo self-test. Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Cre-eD
added a commit
that referenced
this pull request
May 17, 2026
…Dependabot
Two changes flagged by the codex+gemini meta-review of our "reduce
per-consumer pin maintenance" effort (HARDENING.md "Consumer-side pin
reduction — RESOLVED" section captures the full lesson):
## 1. Align all simple-container-com/actions/* refs to one SHA
The api workflows referenced two different actions-repo SHAs after the
incremental composite migration:
- bc4a81b (8x notify-telegram, 4x install-sc, 1x install-welder, 4x
reusable security/semgrep workflows) — pre-#13
- a374c76 (5x setup-sc-tooling, 4x sign-and-attest, 4x install-
attest-tools) — post-#13
Both still resolve on actions/main, so functionally equivalent, but
the split adds cognitive load and makes Dependabot's eventual bumps
land as fragmented PRs. Single sed: every `simple-container-com/
actions/*@<sha>` ref now points at the current actions/main HEAD
(`0af5a697f24ea484991660619d0ae42d50343b9d` — the post-#16 merge),
unified.
## 2. Dependabot grouping for simple-container-com/actions/*
The actual fix for the original "21+ Dependabot PRs per actions-repo
release" pain (which composites + sign-and-attest reduced LOC but not
PR-count). The new `simple-container-com-actions` group in
`.github/dependabot.yml` batches every bump matching the patterns
- simple-container-com/actions
- simple-container-com/actions/*
into ONE PR per upstream release. Pattern-based groups resolve before
update-type groups, so this is independent of major/minor/patch
classification — security advisories on our own shared actions arrive
as a single reviewable PR. The pre-existing actions-minor-and-patch
group continues to batch THIRD-PARTY action bumps the same way.
## Why both changes together
The meta-review (codex + gemini, round 3) converged on the playbook:
Step 1 — Dependabot/Renovate grouping FIRST (solve fatigue at the
bot layer; ~10 LOC config).
Step 2 — Composites SECOND (when actions are semantically paired
and the encapsulated logic is shared).
Step 3 — Reusable workflows ONLY when ≥ 3 consumers AND the
workflow is self-contained AND the job boundary is stable.
We did Steps 2 + 3 first (composites + the now-abandoned sc-image-
release.yml), which was overbuilt one step too early. This commit
adds Step 1 retroactively. With grouping enabled, the user's
original "less than 9 entries" goal is achieved at the PR level
(one PR per release, regardless of `uses:` count) — not at the
workflow-file level.
## actionlint
No new warnings vs baseline (pre-existing blacksmith / shellcheck
nits in existing run: blocks I didn't touch).
Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Cre-eD
added a commit
that referenced
this pull request
May 18, 2026
…adges (#263) ## Summary Three Scorecard sub-10 checks fixed in one bundled PR. Projected delta: **7.8 → ~9.2** on next rescan. | Check | Before | After | Why | |---|---|---|---| | Token-Permissions | 0/10 | **10/10** | `build-staging.yml` + `simple-forge.yml` shipped without a top-level `permissions:` block; both now default to `contents: read`. | | Pinned-Dependencies | 6/10 | **10/10** | Remaining unpinned action refs were first-party `simple-container-com/*` on `@main` / `@v1`. Scorecard treats those identically to third-party. SHA-pinned all of them with a `# main` / `# v1` trailing comment so Dependabot still tracks the symbolic ref. | | README badges | — | **added** | OpenSSF Scorecard + CodeQL + Semgrep + SLSA L3 + Sigstore + Go Report Card + License at the top of `README.md`. OpenSSF Best Practices badge waits for the admin-UI submission tracked in Phase 8. | | Composite consolidation | — | **adopted** | After simple-container-com/actions PR #12 + #13 merged on 2026-05-17, three migrations: (a) five `install-sc + install-welder` PAIRS collapse to `setup-sc-tooling@<sha>`; (b) six `./.github/actions/install-attest-tools` refs swap to the now-shared `simple-container-com/actions/install-attest-tools@<sha>` (api-local copy deleted); (c) the inline 5-step image signing block (Install attest tools + SBOM + cosign sign + cosign attest + SLSA provenance) collapses to one `sign-and-attest@<sha>` call across push.yaml + branch-preview.yaml + build-staging.yml. −155 LOC, byte-identical behaviour at every call site. | ## What changed ### Token-Permissions ```diff + permissions: + contents: read ``` Added to: - `build-staging.yml` (per-job `id-token: write` + `attestations: write` for cosign + attest-build-provenance kept intact) - `simple-forge.yml` (uses a PAT for checkout; no GITHUB_TOKEN write scope needed) ### Pinned-Dependencies | Reference | Old | New | Count | |---|---|---|---| | `simple-container-com/actions/install-sc` | `@main` | `@bc4a81b09e04c41c9ff44f1f07d91634ee687f56 # main` | 5 | | `simple-container-com/actions/install-welder` | `@main` | `@bc4a81b09e04c41c9ff44f1f07d91634ee687f56 # main` | 4 | | `simple-container-com/actions/notify-telegram` | `@main` | `@bc4a81b09e04c41c9ff44f1f07d91634ee687f56 # main` | 4 | | `simple-container-com/actions/.github/workflows/{semgrep,semgrep-comment,security-scan,security-scan-comment}.yml` | `@main` | `@bc4a81b09e04c41c9ff44f1f07d91634ee687f56 # main` | 4 | | `simple-container-com/forge-action/.github/actions/dockerless` | `@v1` | `@b85c96d5f597a28e9dc7528b818c5c79e6575ad5 # v1` | 1 | The 3 remaining `simple-container-com/api/.github/actions/...@v${VERSION}` refs in `branch-preview.yaml` are intentional runtime interpolation against the preview-release tag and can't be SHA-pinned without breaking the preview-version test harness. ### README badges Top-of-README block: ```markdown [](...) [](...) [](...) [](...) [](...) [](...) [](...) ``` ### Composite consolidation (added after #12 + #13 merged) Now that `simple-container-com/actions/setup-sc-tooling` and `simple-container-com/actions/install-attest-tools` are on `actions/main`, the matching call sites in this repo migrate: | Migration | Before | After | Sites | |---|---|---|---| | `install-sc + install-welder` pair collapse | 2 sequential `uses:` per pair | one `setup-sc-tooling` call | push.yaml (×2), branch-preview.yaml, build-staging.yml, branch.yaml | | api-local `install-attest-tools` → shared | `uses: ./.github/actions/install-attest-tools` | `uses: simple-container-com/actions/install-attest-tools@<sha> # main` | push.yaml (×2), branch-preview.yaml (×2), build-staging.yml, verify-attestations.yml | | api-local action removed | `.github/actions/install-attest-tools/action.yml` (35 LOC) | deleted | — | Net diff: −26 LOC, no functional change at any call site. Pinned cosign (v2.4.1) + syft (v1.16.0) versions are identical to the api-local copy — drop-in replacement. **Step C-1 (`sign-and-attest`) is NOT adopted here.** The inline signing blocks in push.yaml / branch-preview.yaml / build-staging.yml are functionally identical to the new composite, but the swap deserves its own preview-build validation cycle. That cycle fits better in the follow-up session that also lands the wrapping `sc-image-release.yml` reusable workflow (Step C-2 in HARDENING.md). ### sign-and-attest adoption (the image signing flow) The actions-repo `sign-and-attest` composite encapsulates the exact 5-step attestation sequence each consumer build workflow inlined per-image: | Step (was) | Composite invocation (now) | |---|---| | Install attestation tools (cosign + syft) | (handled by composite via `install-tools: 'true'` default) | | Generate CycloneDX SBOM via `syft scan registry:<ref>` | (handled) | | `cosign sign --yes <ref>` | (handled) | | `cosign attest --yes --predicate sbom-*.cdx.json --type cyclonedx <ref>` | (handled) | | `actions/attest-build-provenance@v4` | (handled, with `push-to-registry: false` preserved) | Aggregator steps that emit `::warning::` rewrite from per-step `steps.X.outcome` to the composite's outputs (`steps.signattest.outputs.{sbom,sign,attest,slsa}-outcome`). Same semantics: warn if any sub-step is not `success` (or `skipped`). Migration sites: - **push.yaml** (docker-build matrix): 4 images × 5 steps → 1 step per image - **branch-preview.yaml** (image-build matrix): same - **build-staging.yml**: 2 images. Kept the top-level `install-attest-tools` step (both composite invocations share the runner; the two `sign-and-attest` calls use `install-tools: 'false'` so the install is paid once). The composite has its own real end-to-end self-test in the actions repo (`self-test-sign-and-attest.yml` — pushes an image to ghcr.io and runs all three consumer verification commands: `cosign verify`, `cosign verify-attestation --type cyclonedx`, `gh attestation verify`). That validated the contract against real Sigstore on every actions-repo PR since #13 merged, so behaviour parity here is high-confidence. ## Out of scope (follow-up PRs) - **Vulnerabilities (8/10)** — 2 remaining advisories are the documented `aws-sdk-go v1 s3crypto` FPs. Closes via the v1 → v2 migration tracked in HARDENING.md (3 `.go` files in `pkg/clouds/{pulumi/,}aws/`). - **Fuzzing (0/10)** — Go fuzz tests on `pkg/security/cache.go` HMAC parse path. Separate small PR. - **OpenSSF Best Practices badge** — admin-UI submission at https://www.bestpractices.dev/. Self-attest ~67 questions for Passing → +33 for Silver. Roughly 75 min total, gated by the first signed prod release landing on dist (Phase 8 trigger checklist). ## Test plan - [x] `actionlint .github/workflows/*.{yml,yaml}` — clean - [x] Diff stays surgical (only `permissions:` blocks + `@<sha> # <ref>` swaps + README header) - [ ] After merge: Scorecard re-runs on its daily cron; expect Token-Permissions 10/10 + Pinned-Deps 10/10 + overall ~9.2 - [ ] After merge: README badges render correctly on github.com/simple-container-com/api --------- Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
Cre-eD
added a commit
that referenced
this pull request
Aug 22, 2026
…IGH live in github-actions:latest) (#388) ## Summary `simplecontainer/github-actions:latest` ships **python3 3.14.5-r0** — 12 HIGH, 32 MEDIUM, 4 LOW across `python3` / `pyc` / `python3-pyc` / `python3-pycache-pyc0` — while Alpine `v3.24/main` has served `3.14.7-r1` for a while. Our own binary in that image scans **0**; every finding is the base OS layer. The cause is the **build cache**, not a dependency version, so #387 could not have caught it. ## Root cause Release, staging and preview builds all pass `cache-from: type=gha`. The final stage's base is digest-pinned, and its `apk update && apk upgrade --no-cache && apk add …` RUN string never changes. So that layer's cache key is **permanently stable** — the upgrade has not actually executed since the first ever build of that scope. Straight from the release log for `076224a`: ``` --cache-from type=gha,scope=prod-github-actions --cache-to type=gha,mode=max,scope=prod-github-actions #13 [stage-1 2/8] RUN apk update && apk upgrade --no-cache && apk add --no-cache ca-certificates git openssh-client curl jq bash python3 aws-cli ... #13 CACHED ``` Confirmed from inside the published image — the package is available, we just never take it: ``` $ docker run --rm --entrypoint sh simplecontainer/github-actions:latest -c 'apk update -q; apk policy python3' python3 policy: 3.14.5-r0: lib/apk/db/installed 3.14.7-r1: https://dl-cdn.alpinelinux.org/alpine/v3.24/main ``` `--no-cache` on the apk line is unrelated and easy to misread as protection: it governs **apk's own index cache**, not Docker layers. `caddy` and `cloud-helpers` currently look healthy only **by accident** — #387 changed instructions upstream of their upgrade layers (a new base digest, a new `RUN`), which busted the chain. `kubectl` was `CACHED` too and is clean only because its package set happens to contain nothing vulnerable. Nothing about that is a property we should rely on. ## Fix Name the final stage `runtime` in all five Dockerfiles, and pass `no-cache-filters: runtime` to `docker/build-push-action` in `push.yaml`, `build-staging.yml` (both steps) and `branch-preview.yaml`. Scoped to the final stage **deliberately**. The expensive work — the SHA-256-verified Pulumi and gcloud downloads, `gcloud components install`, and the slimming pass — lives in `builder` and keeps its cache. This was the open question ("isn't the cache higher priority?"), so it was measured rather than assumed, on `github-actions.Dockerfile` with a warm builder: | build | wall time | python3 | OS findings | |---|---|---|---| | fully cached (today's behaviour) | **7.3 s** | 3.14.5-r0 | 48 | | `--no-cache-filter runtime` | **41.1 s** | **3.14.7-r1** | **0** | **+34 s per image.** The trust-tier cache scoping added in the earlier hardening work is untouched — `cache-from` / `cache-to` and the `prod-*` / `staging-*` / `preview-*` separation are unchanged, so a lower-trust build still cannot write a scope a release build reads. ## Verification All five images built locally with `--no-cache-filter runtime`, then scanned: | image | OS-package findings | total | remaining findings are | |---|---|---|---| | `github-actions` | **0** (was 48) | 17 | `gcloud-crc32c` + `gke-gcloud-auth-plugin` — Google's binaries | | `github-actions-staging` | **0** | 17 | same | | `cloud-helpers` | **0** | **0** | — | | `caddy` | **0** | 1 | `cel-go` — needs an upstream Caddy change | | `kubectl` | **0** | 17 | upstream `kubectl` binary | Every remaining finding is an upstream binary we do not compile, each already documented in #387. In-Dockerfile smoke tests (`caddy version`, plugin greps, `pulumi version`, `gcloud version`, gke-auth-plugin presence, `aws --version`, `sc` symlink, `test -x /cloud-helpers`) all pass under the filter. YAML parse-checked on all three workflows. ## Why this matters beyond python3 `git`, `openssh-client`, `curl`, `libssl3`, `libcrypto3` and `aws-cli` sit in the **same frozen layer**. The next advisory in any of them would have been just as invisible — in the image that runs every consumer's deploy while holding their cloud credentials. The python3 CVEs themselves are mostly DoS plus two `tarfile` extraction-filter bypasses (`CVE-2026-11940`, `CVE-2026-4360`) that need an attacker-controlled archive, and Python's inputs here are gcloud's authenticated Google API traffic — so live exploitability is low. **The reason to fix is that the patching mechanism was dead, not the severity of what it happened to be hiding this month.** ## Not switching to distroless / scratch Considered and rejected for these images, on evidence rather than preference: ``` $ head -1 $(command -v aws) -> #!/usr/bin/python3 $ head -1 /opt/google-cloud-sdk/bin/gcloud -> #!/bin/sh (needs CLOUDSDK_PYTHON) $ grep -oE '"(gcloud|aws|git|ssh|bash|sh|pulumi)"' -r pkg/ -> bash gcloud git pulumi sh ssh ``` `aws` is a Python script, `gcloud` is a shell script that needs a Python interpreter, and SC shells out to `bash` / `git` / `ssh` / `pulumi` / `gcloud` through Pulumi `local.Command` (`aws s3 sync` in the static-website template, `gcloud auth configure-docker` in the GKE stack). A shell-less, Python-less base cannot run this image — its entire purpose is having those tools on `PATH`. `distroless/python3` would only trade an Alpine base we *can* patch for a Debian base we can't, minus gcloud, git and ssh. Scratch **is** worth considering for `cloud-helpers` (a single static Go binary, already at 0 findings — the gain would be structural immunity to base drift rather than a fix) and possibly `caddy`. Both are separate changes and do not belong in a cache fix. ## Test plan - `docker build --no-cache-filter runtime` for all five Dockerfiles: build **OK**, in-Dockerfile smoke tests pass, 0 OS-package findings each. - Timing measured with a warm builder cache to confirm the builder stage is unaffected: 7.3 s → 41.1 s. - `yaml.safe_load` on `push.yaml`, `build-staging.yml`, `branch-preview.yaml`. - No Go, `go.mod` or test changes — this is Dockerfile stage naming plus one workflow input. - The real proof is the next release: `simplecontainer/github-actions:latest` should come out with `python3 3.14.7-r1` and 0 alpine findings. Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.