Skip to content

ci: harden workflows — SHA-pin actions, drop inlined SSH key, least-privilege - #583

Merged
goosewobbler merged 1 commit into
mainfrom
ci/harden-workflows
Jul 19, 2026
Merged

goosewobbler merged 1 commit into
mainfrom
ci/harden-workflows

Conversation

@goosewobbler

@goosewobbler goosewobbler commented Jul 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

Defense-in-depth hardening of the CI workflows (from the 2026-07-19 security review, #560). No confirmed-exploitable RCE — these are supply-chain and least-privilege tightenings, scoped to .github/workflows/*.yml.

  • Pin the action in the privileged pull_request_target (pr-title.yml, amannn/action-semantic-pull-request) to a full SHA.
  • Remove the inlined SSH key in _release.reusable.yml (git config --global core.sshCommand "ssh -i <key>…") — it inlined key material into a shell command, was buggy (ssh -i expects a file path), and is redundant with actions/checkout's ssh-key:. The deploy key still flows through checkout.
  • Add permissions: { contents: read } to the release.yml check-labels job (read-only gh api calls).
  • Pin the remaining mutable third-party tags (dtolnay/rust-toolchain@stable, dorny/paths-filter@v4, pnpm/action-setup@v5/@v6) to full SHAs so Dependabot can manage them.

First-party actions/* and github/codeql-action are left as-is per the issue's scope; consumer-facing docs/templates keep readable tags.

Test plan

  • SHAs were resolved from each action's currently-pinned tag via gh api repos/<owner>/<repo>/commits/<tag> --jq .sha, verified by the orchestrator (all four spot-checks match), and each pin carries a # <tag> comment.
  • All 20 workflow files parse as valid YAML.
  • pnpm turbo run lint typecheck test — all 32 tasks pass; pnpm lint — exit 0.

Closes #560

🤖 Generated with Claude Code

https://claude.ai/code/session_01BT6fVBZrmExXZj7vQ29881

Greptile Summary

This PR hardens the CI/CD supply chain by SHA-pinning all mutable third-party action tags, removing a broken inlined SSH key step, and adding minimal required permissions to the check-labels job.

  • Four third-party actions (amannn/action-semantic-pull-request, dorny/paths-filter, dtolnay/rust-toolchain, pnpm/action-setup) are pinned to verified full commit SHAs with tag comments, making Dependabot-managed updates possible.
  • The git config core.sshCommand "ssh -i <key>" step in _release.reusable.yml is removed; it was never functional (ssh -i requires a file path, not raw key material) and redundant with the ssh-key: input already passed to actions/checkout.
  • The check-labels job in release.yml gains explicit contents: read and pull-requests: read permissions, the latter required for the gh api repos/.../commits/.../pulls call to succeed.

Confidence Score: 5/5

Safe to merge — all changes are pure hardening with no logic modifications to release or CI orchestration.

Every change is a targeted security improvement: SHA pins replace mutable tags, a broken and redundant SSH inlining step is removed, and a missing permission is added to a read-only job. No business logic is touched, and the pull-requests: read addition directly addresses the previously flagged gap in check-labels.

No files require special attention. release.yml is the most sensitive file changed but its edit is minimal and correct.

Important Files Changed

Filename Overview
.github/workflows/pr-title.yml Pinned amannn/action-semantic-pull-request@v6 to a full commit SHA — the most security-critical pin in this PR because the workflow runs under pull_request_target.
.github/workflows/release.yml Added contents: read and pull-requests: read to the check-labels job; both permissions are required for the gh api repos/.../commits/.../pulls call and the job is correctly scoped.
.github/workflows/_release.reusable.yml Removed the broken/redundant Configure git SSH step that incorrectly passed raw key material (not a file path) to ssh -i; the deploy key continues to flow through actions/checkout's ssh-key: input correctly.
.github/workflows/_ci-detect-changes.reusable.yml SHA-pinned dorny/paths-filter@v4 with a tag comment; no logic changes.
.github/workflows/_ci-integration.reusable.yml SHA-pinned dtolnay/rust-toolchain@stable; Dependabot will now manage Rust toolchain version bumps.
.github/workflows/actions/setup-workspace/action.yml SHA-pinned pnpm/action-setup@v5 in the shared composite action.
.github/workflows/examples-validate.yml SHA-pinned pnpm/action-setup@v6; uses v6 directly (separate from the v5 pin in setup-workspace) with no logic changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[workflow_run: CI on main] --> B[check-labels\ncontents:read + pull-requests:read]
    B -->|gh api commits/SHA/pulls| C{Release label?}
    C -->|yes| D[release-auto\nid-token:write / contents:write]
    C -->|no| E[Skip]
    D --> F[_release.reusable.yml]
    F --> G[Checkout via ssh-key input]
    F --> H[Build + releasekit]
    J[pull_request_target] --> K[pr-title.yml\nSHA-pinned: amannn @48f2562]
    subgraph Pinned Actions
        L[dorny/paths-filter @7b450ff]
        M[dtolnay/rust-toolchain @4cda84d]
        N[pnpm/action-setup v5 @fc06bc1]
        O[pnpm/action-setup v6 @0ebf471]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[workflow_run: CI on main] --> B[check-labels\ncontents:read + pull-requests:read]
    B -->|gh api commits/SHA/pulls| C{Release label?}
    C -->|yes| D[release-auto\nid-token:write / contents:write]
    C -->|no| E[Skip]
    D --> F[_release.reusable.yml]
    F --> G[Checkout via ssh-key input]
    F --> H[Build + releasekit]
    J[pull_request_target] --> K[pr-title.yml\nSHA-pinned: amannn @48f2562]
    subgraph Pinned Actions
        L[dorny/paths-filter @7b450ff]
        M[dtolnay/rust-toolchain @4cda84d]
        N[pnpm/action-setup v5 @fc06bc1]
        O[pnpm/action-setup v6 @0ebf471]
    end
Loading

Reviews (3): Last reviewed commit: "ci: harden workflows — pin actions to SH..." | Re-trigger Greptile

@goosewobbler goosewobbler added this to the 0.42.0 milestone Jul 19, 2026
@github-actions

github-actions Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

Standing release PR: #582 · v0.41.1 queued (4 packages) · open 1h 36m · ✅ ready to merge

Release Preview — v0.41.0 → v0.41.1

Note: Labels on this PR are advisory in standing-pr mode. Bumps come from conventional commits in the standing PR; override by editing labels on the standing PR itself. Add release:immediate to bypass the standing PR and release this PR directly.

These changes will be added to the release PR (#582) when merged:

Changelog

monorepo 0.41.0 → 0.41.1

Changed

  • harden workflows — pin actions to SHAs, drop inlined SSH key, least-privilege (#560)

All packages → 0.41.1 (sync versioning)

  • @releasekit/version
  • @releasekit/notes
  • @releasekit/publish
  • @releasekit/release

After merge — predicted release

No version escalation — this PR's changes will be included in the queued release without affecting the projected versions.

Package Standing PR This PR After merge
monorepo 0.41.1 0.41.1 0.41.1

Updated automatically by ReleaseKit

Comment thread .github/workflows/release.yml Outdated
@goosewobbler
goosewobbler force-pushed the ci/harden-workflows branch from eb76102 to 7111c15 Compare July 19, 2026 22:31
…st-privilege

Pin mutable third-party actions to full commit SHAs (amannn/action-
semantic-pull-request, dtolnay/rust-toolchain, dorny/paths-filter,
pnpm/action-setup) so a repointed tag can't run with CI privileges;
Dependabot's github-actions ecosystem can bump them. Remove the inlined
SSH private key from _release.reusable.yml (buggy and redundant with
actions/checkout's ssh-key:). Scope the release.yml check-labels job to
contents: read.

Closes #560

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BT6fVBZrmExXZj7vQ29881
@goosewobbler
goosewobbler force-pushed the ci/harden-workflows branch from 7111c15 to d4a5bf5 Compare July 19, 2026 22:42
@goosewobbler
goosewobbler merged commit 533889b into main Jul 19, 2026
27 checks passed
@goosewobbler
goosewobbler deleted the ci/harden-workflows branch July 19, 2026 23:01
@github-actions github-actions Bot mentioned this pull request Jul 19, 2026
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.

CI workflow hardening: pin actions to SHAs, remove inlined SSH key, least-privilege

1 participant