Skip to content

feat: add GitHub Actions SHA verification to reusable_ci #337

Description

@marcusburghardt

Summary

Extend reusable_ci.yml with a new verify-action-shas job that validates all
SHA-pinned action references in .github/workflows/*.yml resolve to real commits
in their referenced repositories. This prevents workflows from breaking at runtime
due to invalid, hallucinated, or typo'd commit SHAs.

Motivation

In PR complytime/.github#114,
actions/setup-node was pinned to a SHA that did not exist in the repository. The
YAML was syntactically valid, so all local checks (lint, build, tests) passed. The
error was only discovered when the workflow executed and GitHub could not resolve
the SHA.

This failure mode is increasingly common with AI-assisted workflow authoring, where
agents may write SHAs from training data rather than sourcing them from the API.
No validation exists today -- locally or in CI -- to catch invalid SHAs before merge.

Proposed Change

Add a verify-action-shas job to reusable_ci.yml that runs in parallel with
the existing megalinter job. The job:

  1. Checks out the repository
  2. Parses all .github/workflows/*.yml files for uses: references
  3. Extracts owner/repo@sha patterns (skipping local ./ and docker://
    references)
  4. For each unique owner/repo@sha, verifies the SHA resolves via the GitHub API
  5. When a version comment is present (e.g., # v6.0.2), verifies the SHA
    matches the resolved tag
  6. When a version comment is absent, warns the author to include one as a best
    practice (does not fail)

Input

inputs:
  enable_action_sha_verify:
    description: 'Enable GitHub Actions SHA verification'
    type: boolean
    default: true

Verification Behavior

Condition Result Severity
SHA does not resolve (404) Fail Error
SHA resolves, version comment present, tag matches Pass --
SHA resolves, version comment present, tag mismatch Fail Error
SHA resolves, no version comment Pass with warning Warning
Cross-org private repo (404 but may be auth) Pass with warning Warning
Local action (./path) Skip --
Docker action (docker://) Skip --

Edge Cases

Case Handling
Reusable workflow paths (owner/repo/.github/workflows/file.yml@sha) Strip path, verify against owner/repo
Annotated tags (tag object SHA != commit SHA) Dereference: fetch tag object, follow .object.url to get commit SHA
Lightweight tags (ref points directly to commit) Direct comparison
Rate limiting Deduplicate owner/repo@sha pairs before verification; typical repo has <20 unique refs
Self-referencing (uses: ./.github/workflows/...) Skip (local path)

Failure Mode

The verify-action-shas job SHOULD be configured as a required status check
in branch protection rules. An invalid SHA will break the workflow at runtime
regardless -- failing the check early is strictly better than discovering the
problem post-merge.

Impact on Consumers

  • Zero integration burden: existing consumers get SHA verification
    automatically on next SHA bump to a version that includes this change
  • Opt-out available: enable_action_sha_verify: false disables the check
  • No new permissions: the job needs only contents: read (already granted);
    the default GITHUB_TOKEN authenticates API calls to public repos (5000
    req/hr rate limit)
  • Non-blocking to existing job: runs in parallel with megalinter

Consumers (9 repos)

  • complytime/.github
  • complytime/complyctl
  • complytime/complytime-demos
  • complytime/org-infra
  • complytime/complytime-collector-components
  • complytime/gemara-content-service
  • complytime/complytime-website
  • unbound-force/unbound-force
  • complytime/complytime-providers

Acceptance Criteria

  • reusable_ci.yml has a verify-action-shas job that runs in parallel
    with megalinter
  • Job parses all .github/workflows/*.yml and verifies each SHA-pinned
    action resolves to a real commit
  • Job handles reusable workflow path syntax
    (owner/repo/.github/workflows/file.yml@sha)
  • Job skips local (./) and Docker (docker://) references
  • When a version comment is present, job verifies SHA matches the claimed
    tag (handling annotated tag dereferencing)
  • When a version comment is absent, job emits a ::warning annotation
    recommending the author add one
  • Cross-org private repos that return 404 produce a ::warning, not a
    failure
  • Job is gated behind enable_action_sha_verify input (default: true)
  • Job produces clear ::error annotations with the offending file, line
    number, owner/repo, and SHA
  • Tests: org-infra's own workflows (valid SHAs) pass the check
  • Documentation: README updated with the new input and its purpose

Related

  • Convention pack rule: CI-002 in the Unbound Force ci.md convention
    pack (being developed separately) codifies the authoring-time prevention for
    AI agents and human authors. This CI job is the enforcement layer.
  • Severity classification: The Adversary severity table already classifies
    "unpinned CI action on mutable tag" as HIGH. An invalid SHA is CRITICAL --
    the workflow will fail to execute.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions code

    Type

    No type

    Fields

    Priority

    Low

    Effort

    None yet

    Projects

    Status
    Ready 🚀

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions