Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,26 @@
bindingset[nwo]
private predicate isContainerImage(string nwo) { nwo.regexpMatch("^docker://.+") }

from UsesStep uses, string nwo, string version, Workflow workflow, string name
private predicate getStepContainerName(UsesStep uses, string name) {

Check warning

Code scanning / CodeQL

Predicates starting with "get" or "as" should return a value Warning

This predicate starts with 'get' but does not return a value.
exists(Workflow workflow |
uses.getEnclosingWorkflow() = workflow and
(
workflow.getName() = name
Comment on lines +34 to +38
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

The query’s metadata comment at the top of the file still describes this as a workflow-only check (for example, the @name says “in workflow”), but the query now also analyzes composite action metadata. Please update the header comment to reflect the expanded scope so the rendered query docs aren’t misleading.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated in commit b204603: the query header @name now reflects the expanded scope (workflow or composite action).

or
not exists(workflow.getName()) and workflow.getLocation().getFile().getBaseName() = name
)
)
or
exists(CompositeAction action |
uses.getEnclosingCompositeAction() = action and
name = action.getLocation().getFile().getBaseName()
)
}

from UsesStep uses, string nwo, string version, string name
where
uses.getCallee() = nwo and
uses.getEnclosingWorkflow() = workflow and
(
workflow.getName() = name
or
not exists(workflow.getName()) and workflow.getLocation().getFile().getBaseName() = name
) and
getStepContainerName(uses, name) and
uses.getVersion() = version and
not isTrustedOwner(nwo) and
not (if isContainerImage(nwo) then isPinnedContainer(version) else isPinnedCommit(version)) and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: Composite unpinned tag test
runs:
using: "composite"
steps:
- uses: foo/bar@v2
- uses: foo/bar@25b062c917b0c75f8b47d8469aff6c94ffd89abb
Comment on lines +1 to +6
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

Test coverage for the new composite-action support only exercises action.yml. Since the intended scope is action.yml and action.yaml, add a second composite-action fixture using the .yaml extension with an unpinned uses: ref (and update the expected results) to ensure both metadata filenames are handled.

Copilot uses AI. Check for mistakes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
| .github/actions/unpinned-tag/action.yml:5:13:5:22 | foo/bar@v2 | Unpinned 3rd party Action 'action.yml' step $@ uses 'foo/bar' with ref 'v2', not a pinned commit hash | .github/actions/unpinned-tag/action.yml:5:7:6:60 | Uses Step | Uses Step |
| .github/workflows/actor_trusted_checkout.yml:19:13:19:36 | completely/fakeaction@v2 | Unpinned 3rd party Action 'actor_trusted_checkout.yml' step $@ uses 'completely/fakeaction' with ref 'v2', not a pinned commit hash | .github/workflows/actor_trusted_checkout.yml:19:7:23:4 | Uses Step | Uses Step |
| .github/workflows/actor_trusted_checkout.yml:23:13:23:37 | fakerepo/comment-on-pr@v1 | Unpinned 3rd party Action 'actor_trusted_checkout.yml' step $@ uses 'fakerepo/comment-on-pr' with ref 'v1', not a pinned commit hash | .github/workflows/actor_trusted_checkout.yml:23:7:26:21 | Uses Step | Uses Step |
| .github/workflows/artifactpoisoning21.yml:13:15:13:49 | dawidd6/action-download-artifact@v2 | Unpinned 3rd party Action 'Pull Request Open' step $@ uses 'dawidd6/action-download-artifact' with ref 'v2', not a pinned commit hash | .github/workflows/artifactpoisoning21.yml:13:9:18:6 | Uses Step | Uses Step |
Expand Down
Loading