Skip to content

chore(security): pin Actions to SHAs + enable Dependabot auto-bumps - #2213

Merged
HongmingWang-Rabbit merged 1 commit into
stagingfrom
chore/pin-actions-to-shas
Apr 28, 2026
Merged

HongmingWang-Rabbit merged 1 commit into
stagingfrom
chore/pin-actions-to-shas

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Supply-chain hardening across 23 workflow files. Pin every `uses:` ref to a commit SHA + enable Dependabot for auto-bumps. Companion to Molecule-AI/molecule-controlplane#308.

The risk

Mutable tags (`actions/checkout@v4`) are a known supply-chain attack vector. The tj-actions/changed-files compromise of March 2025 is the canonical example: maintainer credential leak → attacker repointed `@v` tags to a payload that exfiltrated repository secrets. SHA pinning closes that risk.

What this PR does

  • Pin all `uses:` refs to SHAs. `actions/checkout@v4` becomes `actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4`. Trailing `# v` comment preserves readability; SHA makes the ref immutable.
  • Enable Dependabot across four ecosystems: github-actions, gomod (workspace-server), npm (canvas), pip (workspace). Weekly cadence — intentional; daily would generate noise without buying real-time security.

Actions covered (10 distinct, 59 references)

`actions/checkout`, `actions/setup-go`, `actions/setup-python`, `actions/setup-node`, `actions/upload-artifact`, `actions/github-script`, `docker/login-action`, `docker/setup-buildx-action`, `docker/build-push-action`, `github/codeql-action/{init,autobuild,analyze}`, `dorny/paths-filter`, `imjasonh/setup-crane`, `pnpm/action-setup`.

Intentionally excluded

`Molecule-AI/molecule-ci/.github/workflows/disable-auto-merge-on-push.yml@main` — internal org reusable workflow. We control its repo; threat model is different from third-party actions. Conventional to pin to `@main` rather than SHA for internal reusables.

Test plan

  • yaml syntax valid for every modified workflow
  • After merge: existing CI continues to work (no behavior change; SHA points to the same commit `@v4` did at PR-time)
  • Within 1 week: first Dependabot PR opens to bump some pinned SHA — confirms auto-update is wired

🤖 Generated with Claude Code

Supply-chain hardening for the CI pipeline. 23 workflow files
modified, 59 mutable-tag refs replaced with commit SHAs.

The risk

Every `uses:` reference in .github/workflows/*.yml was pinned to a
mutable tag (e.g., `actions/checkout@v4`). A maintainer of an
action — or a compromised maintainer account — can repoint that
tag to malicious code, and our pipelines silently pull it on the
next run. The tj-actions/changed-files compromise of March 2025 is
the canonical example: maintainer credential leak, attacker
repointed several `@v<N>` tags to a payload that exfiltrated
repository secrets. Repos that pinned to SHAs were unaffected.

The fix

Replace each `@v<N>` with `@<commit-sha> # v<N>`. The trailing
comment preserves human readability ("ah, this is v4"); the SHA
makes the reference immutable.

Actions covered (10 distinct):
  actions/{checkout,setup-go,setup-python,setup-node,upload-artifact,github-script}
  docker/{login-action,setup-buildx-action,build-push-action}
  github/codeql-action/{init,autobuild,analyze}
  dorny/paths-filter
  imjasonh/setup-crane
  pnpm/action-setup (already pinned in molecule-app, listed here for completeness)

Excluded:
  Molecule-AI/molecule-ci/.github/workflows/disable-auto-merge-on-push.yml@main
    — internal org reusable workflow; we control its repo, threat model
    is different from third-party actions. Conventional to pin to @main
    rather than SHA for internal reusables.

The maintenance cost

SHA pinning means upstream fixes require manual SHA bumps. Without
automation, pinned SHAs go stale. So this PR also enables Dependabot
across four ecosystems:

  - github-actions (workflows)
  - gomod (workspace-server)
  - npm (canvas)
  - pip (workspace runtime requirements)

Weekly cadence — the supply-chain attack window is "minutes between
repoint and pull"; weekly auto-bumps don't help with zero-days
regardless. The point is to pull in non-zero-day fixes without
operator effort.

Aligns with user-stated principle: "long-term, robust, fully-
automated, eliminate human error."

Companion PR: Molecule-AI/molecule-controlplane#308 (same pattern,
smaller surface).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue Apr 28, 2026
Merged via the queue into staging with commit 1867111 Apr 28, 2026
25 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the chore/pin-actions-to-shas branch April 28, 2026 22:53
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
… + settle /buildinfo (#2213)

Root cause of the #2213 main-red (`publish-workspace-server-image /
Production auto-deploy` failing on hongming "is stale"):

Two main pushes landed ~2 min apart (7a72516 then 7f25373). With no
`concurrency:` on this workflow (intentional — Gitea 1.22.6 cancels queued
prod deploys) BOTH deploy-production jobs run. The OLDER 7a72516 job started
late, after 7f25373 was already main's head. The #2194 superseded guard only
protected the *verify* step — it ran AFTER the redeploy and the :latest
promote. So the older job still:
  1. redeployed the canary (hongming) BACKWARD to staging-7a72516, reverting
     it from the newer SHA the 7f25373 job had just shipped — which is exactly
     what the 7f25373 job's verify then saw ("hongming is stale: actual=7a72516,
     expected=7f25373") -> main red; AND
  2. promoted :latest BACKWARD to the older staging-7a72516 image,
before finally skipping verify and exiting green.

Fix (defense in depth, no change to the redeploy/rollout logic itself):
- Add a "Check superseded before production side effects" step that runs the
  existing check-superseded BEFORE the rollout. When a newer commit already
  owns main, gate OFF both the redeploy-fleet step and the :latest promote so
  an older job never rolls the fleet (or :latest) backward. Fail-safe: an
  unreadable head is treated as NOT superseded, so a genuine deploy never
  silently skips. The in-step verify guard is kept to catch a newer job that
  lands DURING this job's rollout.
- Harden the /buildinfo verify with a bounded per-tenant settle budget
  (default 240s, 20s interval, both overridable via repo vars). `curl --retry`
  only retries connection/5xx failures, not a stale-but-200 body, so a tenant
  whose container the CP just swapped — still serving the draining old image
  at the edge — false-reds "stale" on the first poll. Now we poll until the
  tenant reports the target SHA or the budget is exhausted, then fail loud.
  A genuinely stuck tenant is NOT masked.

Tests: pin the superseded contract for the exact 7a72516/7f25373 incident
shape (older job superseded -> skip; latest job -> still rolls + verifies).
All 35 prod-auto-deploy unit tests pass; lint-workflow-yaml + curl-status
linters clean; every run block bash -n clean.

Refs #2213
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…g the fleet backward + settle /buildinfo (#2213)' (#2215) from fix/prod-deploy-verify-tenant-lag-2213 into main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant