Skip to content

Latest commit

 

History

History
49 lines (31 loc) · 2.11 KB

File metadata and controls

49 lines (31 loc) · 2.11 KB

SHA-pinned action requires a version comment

This error means a workflow contains a uses: line pinned to a full commit SHA but missing the trailing # <version> comment, and pinact cannot cross-verify the SHA against any upstream tag.

# Error: bare SHA with no comment
- uses: owner/repo@e7500cdc5165d144721128822d9018f7be4eb43e

# OK: SHA backed by an upstream tag
- uses: owner/repo@e7500cdc5165d144721128822d9018f7be4eb43e # v1.2.3

Why is this an error?

owner/repo@<SHA> is resolved across the entire GitHub fork network. An attacker who lacks push access to the upstream repository can still push a commit to a fork, producing a <SHA> that the uses: line will happily resolve to. Without an accompanying tag comment, neither pinact nor a human reviewer can tell whether the SHA belongs to the upstream repository or to a fork.

When the version comment is present, pinact's --verify-comment mode confirms that the SHA matches the SHA actually pointed to by the named tag on the upstream repository, closing the fork-network gap.

A secondary benefit: bare SHAs that have been removed from the upstream history (deleted tags, rebased branches) are silently undetectable. Requiring a verifiable comment surfaces them too.

How to resolve

1. Let pinact add the comment

If the SHA corresponds to an upstream tag, run pinact without -no-api:

pinact run

pinact will look up the tag and rewrite the line as @<SHA> # <tag>.

2. Drop -no-api

-no-api disables the GitHub API call that resolves SHA to tag. Without it pinact has no way to verify the SHA, so any SHA-pinned action without a comment is rejected. Re-run without -no-api (at least once) to populate the comments.

3. Ignore legitimate untagged SHAs

For SHAs that intentionally have no upstream tag (internal forks, pre-release pins, vendored composite actions), opt out with rules in .pinact.yaml:

rules:
  - ignore: true
    conditions:
      - expr: |
          ActionName == "owner/repo" && ActionVersion == "e7500cdc5165d144721128822d9018f7be4eb43e"

The matched action is skipped entirely, including this check.