Make CodeQL Check First-Party GitHub Actions for SHA Pinning

CodeQL passes your workflow even though actions/checkout@v4 is a movable tag, not a commit SHA. Why are GitHub’s own actions skipped, and how do you bring them into the check?

Make CodeQL Check First-Party GitHub Actions for SHA Pinning
TLDR
  • CodeQL skips selected GitHub-owned publishers when checking for unpinned action tags.
  • A model pack can remove that exception and bring first-party actions into the check.
  • Load the pack alongside a query suite containing the check. References already pinned by an Actions lockfile remain excluded.

Why actions/checkout@v4 can pass without a warning

The v4 tag can move to another commit without any change to your workflow. CodeQL nevertheless skips actions, github and advanced-security in its actions/unpinned-tag query because those publishers are trusted by default.

CodeQL 2.27.2 added an override for that publisher exception. Removing it lets the query inspect first-party references too. This produces analysis findings; it does not block an action from running.

Prefix each owner you want to remove with !

In a model extension, trustedActionsOwnerDataModel lists publishers exempt from the query. A plain owner name adds an exception. Prefixing it with ! removes the exception, so !actions brings actions published by actions into the check.

To include all three publishers, put this example in a model pack’s models/trusted-owner.model.json:

{
  "extensions": [{
    "addsTo": {
      "pack": "codeql/actions-all",
      "extensible": "trustedActionsOwnerDataModel"
    },
    "data": [["!actions"], ["!github"], ["!advanced-security"]]
  }]
}

For actions/checkout, the owner is actions, so the relevant entry is !actions. Use !github for actions owned by github. If both a plain and a prefixed entry exist for one owner, the removal wins.

Point codeql-pack.yml at the JSON extension. Replace the example pack name with the name you will publish:

name: example-org/actions-owner-policy
version: 0.0.1
library: true
extensionTargets:
  codeql/actions-all: '*'
dataExtensions:
  - models/**/*.json

Include both the model pack and the query

After publishing the pack and making it accessible, CLI analysis can load it with --model-packs. This example assumes an existing Actions database; replace the database path and pack name:

codeql database analyze ./actions-db \
  codeql/actions-queries:codeql-suites/actions-security-extended.qls \
  --download --model-packs example-org/actions-owner-policy \
  --format=sarif-latest --output=actions-results.sarif

The command loads your publisher override through --model-packs and runs the unpinned-tag query through the extended suite. The query also belongs to actions-security-and-quality.qls.

The query implementation excludes references already pinned by an Actions lockfile and actions recognized as immutable. Those exclusions still apply after you remove publisher trust. The data-extension format is currently a beta API.

Use the original repository’s SHA and keep it updated

For a mutable reference you intend to pin directly, select the full commit SHA for the desired release. GitHub’s guidance calls for verifying that the commit belongs to the action’s repository, not a fork.

A SHA-pinned workflow keeps running the same commit when a new release appears. To receive a fix, it needs an updated SHA. The CodeQL Action README recommends tools such as Dependabot to keep pinned versions current.