Skip to content

ci: harden GitHub Actions workflows#208

Merged
jeremy merged 19 commits into
mainfrom
flavorjones/harden-github-actions
Mar 20, 2026
Merged

ci: harden GitHub Actions workflows#208
jeremy merged 19 commits into
mainfrom
flavorjones/harden-github-actions

Conversation

@flavorjones
Copy link
Copy Markdown
Member

@flavorjones flavorjones commented Mar 19, 2026

Summary

  • Pin all actions to SHA hashes with version comments via pinact
  • Fix zizmor findings by severity: bot-conditions, template-injection, excessive-permissions, dangerous-triggers, cache-poisoning, artipacked, superfluous-actions, dependabot-cooldown
  • Move workflow-level permissions to per-job least-privilege grants with permissions: {} at workflow level
  • Add zizmor + actionlint CI job to test workflow
  • Add 10-day cooldown to all dependabot ecosystem entries

Test plan

  • CI passes on this branch
  • Verify zizmor reports clean: zizmor .

Summary by cubic

Hardens all GitHub Actions workflows by pinning actions to commit SHAs, enforcing deny‑all permissions by default, and consolidating security linting. Reduces supply‑chain risk and resolves all current zizmor findings.

  • New Features

    • Added a consolidated lint-actions job in test.yml that runs actionlint and zizmor; removed the duplicate actionlint job.
  • Refactors

    • Pinned all actions (e.g., actions/checkout, actions/setup-*, github/codeql-action, gradle/actions/setup-gradle, ruby/setup-ruby, golangci/golangci-lint-action, actions/cache, softprops/action-gh-release) to exact SHAs with version comments.
    • Enforced least-privilege: set permissions: {} at the workflow level and granted minimal per‑job permissions; added persist-credentials: false to all actions/checkout steps unless a push is required.
    • Fixed security findings: use github.event.pull_request.user.login for Dependabot checks, replace inline base ref expression with GITHUB_BASE_REF, add safe pull_request_target notes, and document justified zizmor ignores (e.g., cache isolation, release actions).
    • Added a 10‑day Dependabot cooldown for all ecosystems to reduce update churn.

Written for commit 7b3da82. Summary will update on new commits.

Update action version comments to reflect exact semver versions.
- Fix bot-conditions: use github.event.pull_request.user.login instead of
  github.actor for dependabot check
- Fix template-injection: use GITHUB_BASE_REF env var instead of inline expression
- Fix excessive-permissions: set permissions: {} at workflow level and scope
  per job in ai-labeler, labeler, release-github, release-kotlin, release-ruby,
  release-typescript, sensitive-change-gate
- Suppress cache-poisoning: branch-isolated caches cannot be poisoned by fork PRs
- Suppress dangerous-triggers: pull_request_target workflows only run trusted
  actions with no PR code checkout or execution
- Fix artipacked: add persist-credentials: false to all checkout steps that
  do not require git push; suppress artipacked on release-go tag job which
  needs credentials for git push
- Fix excessive-permissions: set permissions: {} at workflow level and scope
  per job in codeql, security, test, release-swift, and smithy-verify
- Fix dependabot-cooldown: add cooldown: default-days: 10 to all ecosystem
  entries in dependabot.yml
- Suppress cache-poisoning: branch-isolated npm caches in release-typescript
- Suppress superfluous-actions on softprops/action-gh-release: actions are
  more maintainable than inline shell code
Add lint-actions job to test.yml near the existing actionlint job to
continuously audit workflow security with zizmor.
Copilot AI review requested due to automatic review settings March 19, 2026 20:22
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 19, 2026

Sensitive Change Detection (shadow mode)

This PR modifies control-plane files:

  • .github/workflows/ai-labeler.yml
  • .github/workflows/codeql.yml
  • .github/workflows/dependabot-auto-merge.yml
  • .github/workflows/direct-push-alert.yml
  • .github/workflows/labeler.yml
  • .github/workflows/release-github.yml
  • .github/workflows/release-go.yml
  • .github/workflows/release-kotlin.yml
  • .github/workflows/release-ruby.yml
  • .github/workflows/release-swift.yml
  • .github/workflows/release-typescript.yml
  • .github/workflows/scorecard.yml
  • .github/workflows/security.yml
  • .github/workflows/sensitive-change-gate.yml
  • .github/workflows/smithy-verify.yml
  • .github/workflows/test.yml

Shadow mode — this check is informational only. When activated, changes to these paths will require approval from a maintainer.

@github-actions github-actions Bot added github-actions Pull requests that update GitHub Actions enhancement New feature or request labels Mar 19, 2026
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/test.yml">

<violation number="1" location=".github/workflows/test.yml:21">
P2: The `lint-actions` job duplicates the actionlint step that already runs in the dedicated `actionlint` job. Every CI run will execute actionlint twice on separate runners. Remove the duplicate step from `lint-actions` so it only runs zizmor.</violation>
</file>

<file name=".github/workflows/ai-labeler.yml">

<violation number="1" location=".github/workflows/ai-labeler.yml:58">
P2: The `spec-impact` job may need `issues: write` to execute the `updateIssueComment` GraphQL mutation. PR comments are issue comments at the API level, and this mutation operates in the issues domain. Both `classify` and `breaking` jobs in this workflow include `issues: write` — its omission here looks like an oversight that could cause the "update existing comment" path to fail with a 403.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/ai-labeler.yml
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the repository’s GitHub Actions and Dependabot configuration by pinning actions, tightening permissions to least-privilege, and adding workflow static analysis (zizmor/actionlint) to CI.

Changes:

  • Pin GitHub Actions to SHA hashes and disable persisted checkout credentials in most jobs.
  • Shift workflow-level permissions to permissions: {} and grant scoped job-level permissions.
  • Add/extend CI auditing with actionlint + zizmor; add Dependabot cooldowns and adjust Dependabot auto-merge bot condition.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
.github/workflows/test.yml Adds actionlint + zizmor auditing and applies least-privilege permissions/pinned actions across CI jobs.
.github/workflows/smithy-verify.yml Pins checkout/setup-java/setup-smithy actions and disables persisted checkout credentials.
.github/workflows/sensitive-change-gate.yml Adds zizmor rationale for pull_request_target and moves permissions to {} + job-scoped grants.
.github/workflows/security.yml Moves to job-scoped permissions and pins actions; keeps SARIF upload permissions scoped.
.github/workflows/scorecard.yml Pins checkout and SARIF upload action versions.
.github/workflows/release-typescript.yml Moves to workflow permissions: {}, pins actions, and disables persisted credentials for checkout.
.github/workflows/release-swift.yml Moves to workflow permissions: {}, pins checkout, and disables persisted credentials for checkout.
.github/workflows/release-ruby.yml Moves to workflow permissions: {}, pins actions, and disables persisted credentials for checkout.
.github/workflows/release-kotlin.yml Moves to workflow permissions: {}, pins actions, and disables persisted credentials for checkout.
.github/workflows/release-go.yml Pins actions and disables persisted credentials in the test job; retains credentials where git pushes are required.
.github/workflows/release-github.yml Moves to workflow permissions: {}, scopes job permissions, pins actions, and disables persisted credentials for checkout.
.github/workflows/labeler.yml Adds zizmor rationale for pull_request_target and moves permissions to job-level.
.github/workflows/dependabot-auto-merge.yml Tightens Dependabot detection condition and pins fetch-metadata action version comment.
.github/workflows/codeql.yml Moves permissions to job-level least-privilege and pins CodeQL-related actions.
.github/workflows/ai-labeler.yml Adds zizmor rationale for pull_request_target, moves permissions to {} + job-scoped grants, and pins checkout.
.github/dependabot.yml Adds a 10-day cooldown across ecosystems to reduce Dependabot update frequency.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/test.yml
Comment thread .github/workflows/release-go.yml
Comment thread .github/workflows/release-typescript.yml
Comment thread .github/workflows/release-ruby.yml
Comment thread .github/workflows/release-kotlin.yml
Comment thread .github/workflows/release-swift.yml
Comment thread .github/workflows/release-github.yml
Replace overly broad workflow-level permissions with deny-all `permissions: {}`
and move permissions into each job that needs them.
Copilot AI review requested due to automatic review settings March 19, 2026 21:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

jeremy added 8 commits March 20, 2026 02:52
Defense-in-depth: verify both github.actor and
github.event.pull_request.user.login are dependabot[bot].
Actor validates the current trigger, user.login validates
PR origin.
Workflow-level permissions: {} (deny-all), with contents: read
granted explicitly on the smithy and test jobs. The tag job
already had its own permissions: contents: write block.
Bump gosec to v2.23.0 and output SARIF format for upload to
GitHub Security tab. Adds security-events: write permission to
support the upload.
Scans current checkout (--no-git) for leaked secrets using
gitleaks v8.22.0, installed via Go for integrity verification
through the module proxy and checksum database.
Runs on pull requests only, advisory mode (continue-on-error)
since it requires GitHub Advanced Security which may not be
available on all plans.
bump-version.sh and sync-api-version.sh are release-critical
control-plane scripts that should trigger review comments on PRs.
Allows low-risk patches through faster (2 days) while giving
major bumps more soak time (7 days). Applied to all 10
ecosystem entries.
Runs actionlint and zizmor locally, gated by command-availability
checks with install instructions. Wired into the check
meta-target so workflow issues are caught alongside other checks.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

.github/workflows/security.yml:316

  • actions/dependency-review-action typically requires pull-requests: read (in addition to contents: read) to fetch the PR diff/dependency changes via the API. With workflow-level permissions: {} and only contents: read here, this job is likely to error or produce no results; add pull-requests: read to this job’s permissions.
  dependency-review:
    name: Dependency Review
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    continue-on-error: true  # Requires GitHub Advanced Security
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0

.github/workflows/test.yml:306

  • The golangci/golangci-lint-action step pins the action itself, but with: version: latest makes the downloaded golangci-lint binary non-deterministic over time and can undermine the workflow hardening goal. Consider pinning version to a specific golangci-lint release (or otherwise making the tool version reproducible).
      - name: golangci-lint
        uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
        with:
          working-directory: go
          version: latest
          args: --config .golangci.yml

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/security.yml">

<violation number="1" location=".github/workflows/security.yml:135">
P2: The SARIF file is written under `go/` due to the job’s working directory, but the upload step points to the repo root. This will skip uploading gosec results. Use the path under `go/`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread .github/workflows/security.yml Outdated
gosec: Revert to v2.22.4 — v2.23.0 introduced G703/G704 taint
analysis rules that are false positives for an HTTP client SDK.
Fix SARIF upload path to go/gosec-results.sarif to account for
the job's working-directory.

Dependabot: Remove semver-granular cooldown keys from the
github-actions ecosystem entry — they are not supported for
that package ecosystem.
The semver-specific cooldown keys are only unsupported for the
github-actions ecosystem. Restore them for the other 9 entries
(gomod, gradle, npm, bundler) where they are valid.
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

v2.23.0 adds G703 (path traversal) and G704 (SSRF) taint
analysis rules. Both are architectural false positives for an
HTTP client SDK: URL construction from caller-provided parameters
and file cache paths from caller-configured directories are core
functionality, not vulnerabilities. Exclude at the CLI level
rather than sprinkling #nosec across 22 call sites.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ai-labeler.yml
Comment thread .github/workflows/security.yml
Comment thread .github/workflows/scorecard.yml
Replace the workflow-level -exclude=G703,G704 flag with targeted
#nosec annotations at each of the 22 call sites. This preserves
taint analysis coverage for future code while suppressing the
known false positives:

- G704 (SSRF): 7 httpClient.Do() calls — an HTTP client SDK
  constructs URLs from caller-provided parameters by design
- G703 (path traversal): 15 file operations on caller-configured
  cache and credentials directories

Also switch gitleaks from go install (which panics with wasm
error in go-re2) to a pre-built release binary with SHA256
checksum verification.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile
Comment thread .github/workflows/scorecard.yml
Comment thread .github/workflows/security.yml
@jeremy jeremy merged commit c487b50 into main Mar 20, 2026
53 checks passed
@jeremy jeremy deleted the flavorjones/harden-github-actions branch March 20, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request github-actions Pull requests that update GitHub Actions go

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants