From ef1bde7565565cfa42769a900c681ee42922bafa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 10:17:50 +0000 Subject: [PATCH 1/8] Widen pinned SHA regex to support SHA-256 (64-char hex) and add tests --- actions/ql/examples/snippets/uses_pinned_sha.ql | 2 +- actions/ql/lib/codeql/actions/Bash.qll | 2 +- actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql | 4 +++- .../Security/CWE-829/.github/workflows/unpinned_tags.yml | 6 ++++++ .../Security/CWE-829/UnpinnedActionsTag.expected | 1 + 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/actions/ql/examples/snippets/uses_pinned_sha.ql b/actions/ql/examples/snippets/uses_pinned_sha.ql index 84b2cdae0fdb..db055a69fc4f 100644 --- a/actions/ql/examples/snippets/uses_pinned_sha.ql +++ b/actions/ql/examples/snippets/uses_pinned_sha.ql @@ -8,5 +8,5 @@ import actions from UsesStep uses -where uses.getVersion().regexpMatch("^[A-Fa-f0-9]{40}$") +where uses.getVersion().regexpMatch("^[A-Fa-f0-9]{40}([A-Fa-f0-9]{24})?$") select uses, "This 'uses' step has a pinned SHA version." diff --git a/actions/ql/lib/codeql/actions/Bash.qll b/actions/ql/lib/codeql/actions/Bash.qll index 4975ce6f4cc5..3ba6c705f3ea 100644 --- a/actions/ql/lib/codeql/actions/Bash.qll +++ b/actions/ql/lib/codeql/actions/Bash.qll @@ -785,7 +785,7 @@ module Bash { /** * Holds if the given regex is used to match an alphanumeric string - * eg: `^[0-9a-zA-Z]{40}$`, `^[0-9]+$` or `^[a-zA-Z0-9_]+$` + * eg: `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$`, `^[0-9]+$` or `^[a-zA-Z0-9_]+$` */ string alphaNumericRegex() { result = "^\\^\\[([09azAZ_-]+)\\](\\+|\\{\\d+\\})\\$$" } } diff --git a/actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql b/actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql index c8512e5a1c0c..283680752020 100644 --- a/actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql +++ b/actions/ql/src/Security/CWE-829/UnpinnedActionsTag.ql @@ -15,7 +15,9 @@ import actions import codeql.actions.security.UseOfUnversionedImmutableAction bindingset[version] -private predicate isPinnedCommit(string version) { version.regexpMatch("^[A-Fa-f0-9]{40}$") } +private predicate isPinnedCommit(string version) { + version.regexpMatch("^[A-Fa-f0-9]{40}([A-Fa-f0-9]{24})?$") +} bindingset[nwo] private predicate isTrustedOwner(string nwo) { diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/unpinned_tags.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/unpinned_tags.yml index f204816eed4e..6e7612144bcc 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/unpinned_tags.yml +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/unpinned_tags.yml @@ -11,3 +11,9 @@ jobs: - uses: foo/bar@25b062c917b0c75f8b47d8469aff6c94ffd89abb - uses: docker://foo/bar@latest - uses: docker://foo/bar@sha256:887a259a5a534f3c4f36cb02dca341673c6089431057242cdc931e9f133147e9 + # SHA-256 pinned (64 hex chars) - should NOT be flagged + - uses: foo/bar@25b062c917b0c75f8b47d8469aff6c94ffd89abb25b062c917b0c75f8b47d84d + # SHA-1 pinned (40 hex chars) regression - should NOT be flagged + - uses: foo/bar@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 + # Invalid 50-char hex string - should be flagged + - uses: foo/bar@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5 diff --git a/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected b/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected index ed35f1546171..a9a1d78de28d 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected @@ -33,3 +33,4 @@ | .github/workflows/test18.yml:37:21:37:63 | sonarsource/sonarcloud-github-action@master | Unpinned 3rd party Action 'Sonar' step $@ uses 'sonarsource/sonarcloud-github-action' with ref 'master', not a pinned commit hash | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Uses Step | | .github/workflows/unpinned_tags.yml:10:13:10:22 | foo/bar@v1 | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'foo/bar' with ref 'v1', not a pinned commit hash | .github/workflows/unpinned_tags.yml:10:7:11:4 | Uses Step | Uses Step | | .github/workflows/unpinned_tags.yml:12:13:12:35 | docker://foo/bar@latest | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'docker://foo/bar' with ref 'latest', not a pinned commit hash | .github/workflows/unpinned_tags.yml:12:7:13:4 | Uses Step | Uses Step | +| .github/workflows/unpinned_tags.yml:19:13:19:70 | foo/bar@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5 | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'foo/bar' with ref 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5', not a pinned commit hash | .github/workflows/unpinned_tags.yml:19:7:19:70 | Uses Step | Uses Step | From 48b1dad959811806f090be171e7c35822bb317ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 10:18:31 +0000 Subject: [PATCH 2/8] Add change note for SHA-256 pinned actions support --- .../ql/src/change-notes/2026-05-12-sha256-pinned-actions.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 actions/ql/src/change-notes/2026-05-12-sha256-pinned-actions.md diff --git a/actions/ql/src/change-notes/2026-05-12-sha256-pinned-actions.md b/actions/ql/src/change-notes/2026-05-12-sha256-pinned-actions.md new file mode 100644 index 000000000000..521a5878c371 --- /dev/null +++ b/actions/ql/src/change-notes/2026-05-12-sha256-pinned-actions.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `actions/unpinned-tag` query now recognizes 64-character SHA-256 commit hashes as properly pinned references, in addition to 40-character SHA-1 hashes. From 0620d348b25d817dcb437117b7881e19b5117b52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 12:51:15 +0000 Subject: [PATCH 3/8] Update Bash alphaNumericRegex to match grouped quantified forms --- actions/ql/lib/codeql/actions/Bash.qll | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/actions/ql/lib/codeql/actions/Bash.qll b/actions/ql/lib/codeql/actions/Bash.qll index 3ba6c705f3ea..a547bf05b77f 100644 --- a/actions/ql/lib/codeql/actions/Bash.qll +++ b/actions/ql/lib/codeql/actions/Bash.qll @@ -787,5 +787,20 @@ module Bash { * Holds if the given regex is used to match an alphanumeric string * eg: `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$`, `^[0-9]+$` or `^[a-zA-Z0-9_]+$` */ - string alphaNumericRegex() { result = "^\\^\\[([09azAZ_-]+)\\](\\+|\\{\\d+\\})\\$$" } + string alphaNumericRegex() { + exists(string r1, string r2, string r3, string r4 | + // An alphanumeric character class + r1 = "\\[([09azAZ_-]+)\\]" and + // The same as above, followed by a quantifier like `+` or `{20}` + r2 = r1 + "(\\+|\\{\\d+\\})" and + // The same as above, possibly with brackets around it + r3 = "\\(?" + r2 + "\\)?" and + // The same as above, possibly with a `?` after it + r4 = r3 + "\\??" + | + // The same as above, repeated one or more times, and with `^` at the + // beginning and `$` at the end + result = "^\\^(" + r4 + ")+\\$$" + ) + } } From 562f415f647b5ea36e887076d387b62e0d83da82 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 12:52:00 +0000 Subject: [PATCH 4/8] Tidy Bash alphaNumericRegex comment spacing --- actions/ql/lib/codeql/actions/Bash.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/ql/lib/codeql/actions/Bash.qll b/actions/ql/lib/codeql/actions/Bash.qll index a547bf05b77f..2caf829383cf 100644 --- a/actions/ql/lib/codeql/actions/Bash.qll +++ b/actions/ql/lib/codeql/actions/Bash.qll @@ -791,7 +791,7 @@ module Bash { exists(string r1, string r2, string r3, string r4 | // An alphanumeric character class r1 = "\\[([09azAZ_-]+)\\]" and - // The same as above, followed by a quantifier like `+` or `{20}` + // The same as above, followed by a quantifier like `+` or `{20}` r2 = r1 + "(\\+|\\{\\d+\\})" and // The same as above, possibly with brackets around it r3 = "\\(?" + r2 + "\\)?" and From 2067113177de479da295fce9efd201fe99b70934 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 May 2026 14:59:46 +0100 Subject: [PATCH 5/8] Update expected test output --- .../query-tests/Security/CWE-829/UnpinnedActionsTag.expected | 2 +- .../Security/CWE-829/UntrustedCheckoutCritical.expected | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected b/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected index a9a1d78de28d..b55345c8f77a 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UnpinnedActionsTag.expected @@ -33,4 +33,4 @@ | .github/workflows/test18.yml:37:21:37:63 | sonarsource/sonarcloud-github-action@master | Unpinned 3rd party Action 'Sonar' step $@ uses 'sonarsource/sonarcloud-github-action' with ref 'master', not a pinned commit hash | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Uses Step | | .github/workflows/unpinned_tags.yml:10:13:10:22 | foo/bar@v1 | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'foo/bar' with ref 'v1', not a pinned commit hash | .github/workflows/unpinned_tags.yml:10:7:11:4 | Uses Step | Uses Step | | .github/workflows/unpinned_tags.yml:12:13:12:35 | docker://foo/bar@latest | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'docker://foo/bar' with ref 'latest', not a pinned commit hash | .github/workflows/unpinned_tags.yml:12:7:13:4 | Uses Step | Uses Step | -| .github/workflows/unpinned_tags.yml:19:13:19:70 | foo/bar@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5 | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'foo/bar' with ref 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5', not a pinned commit hash | .github/workflows/unpinned_tags.yml:19:7:19:70 | Uses Step | Uses Step | +| .github/workflows/unpinned_tags.yml:19:13:19:70 | foo/bar@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5 | Unpinned 3rd party Action 'unpinned_tags.yml' step $@ uses 'foo/bar' with ref 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2a1b2c3d4e5', not a pinned commit hash | .github/workflows/unpinned_tags.yml:19:7:19:71 | Uses Step | Uses Step | diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 39e54b2bbaed..7cf5657d2e80 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -311,7 +311,10 @@ edges | .github/workflows/unpinned_tags.yml:9:7:10:4 | Uses Step | .github/workflows/unpinned_tags.yml:10:7:11:4 | Uses Step | | .github/workflows/unpinned_tags.yml:10:7:11:4 | Uses Step | .github/workflows/unpinned_tags.yml:11:7:12:4 | Uses Step | | .github/workflows/unpinned_tags.yml:11:7:12:4 | Uses Step | .github/workflows/unpinned_tags.yml:12:7:13:4 | Uses Step | -| .github/workflows/unpinned_tags.yml:12:7:13:4 | Uses Step | .github/workflows/unpinned_tags.yml:13:7:13:101 | Uses Step | +| .github/workflows/unpinned_tags.yml:12:7:13:4 | Uses Step | .github/workflows/unpinned_tags.yml:13:7:15:4 | Uses Step | +| .github/workflows/unpinned_tags.yml:13:7:15:4 | Uses Step | .github/workflows/unpinned_tags.yml:15:7:17:4 | Uses Step | +| .github/workflows/unpinned_tags.yml:15:7:17:4 | Uses Step | .github/workflows/unpinned_tags.yml:17:7:19:4 | Uses Step | +| .github/workflows/unpinned_tags.yml:17:7:19:4 | Uses Step | .github/workflows/unpinned_tags.yml:19:7:19:71 | Uses Step | | .github/workflows/untrusted_checkout2.yml:7:9:14:6 | Run Step: pr_number | .github/workflows/untrusted_checkout2.yml:14:9:19:72 | Run Step | | .github/workflows/untrusted_checkout3.yml:11:9:12:6 | Uses Step | .github/workflows/untrusted_checkout3.yml:12:9:13:6 | Uses Step | | .github/workflows/untrusted_checkout3.yml:12:9:13:6 | Uses Step | .github/actions/dangerous-git-checkout/action.yml:6:7:11:4 | Uses Step | From f58268064ed13552a565e2fdee0d4f3ab40f1b79 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 May 2026 15:23:05 +0100 Subject: [PATCH 6/8] Add change note for alphanumeric regex change --- .../change-notes/2026-05-12-improved-alphanumeric-regex.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md diff --git a/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md b/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md new file mode 100644 index 000000000000..b87890a70ac0 --- /dev/null +++ b/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. \ No newline at end of file From ea29986c4f44c59903f92073a4265d6be4569da8 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> Date: Tue, 12 May 2026 16:45:50 +0100 Subject: [PATCH 7/8] Fix non-US english by using "parentheses" instead of "brackets" Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- actions/ql/lib/codeql/actions/Bash.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/ql/lib/codeql/actions/Bash.qll b/actions/ql/lib/codeql/actions/Bash.qll index 2caf829383cf..14ba8f0acf4a 100644 --- a/actions/ql/lib/codeql/actions/Bash.qll +++ b/actions/ql/lib/codeql/actions/Bash.qll @@ -793,7 +793,7 @@ module Bash { r1 = "\\[([09azAZ_-]+)\\]" and // The same as above, followed by a quantifier like `+` or `{20}` r2 = r1 + "(\\+|\\{\\d+\\})" and - // The same as above, possibly with brackets around it + // The same as above, possibly with parentheses around it r3 = "\\(?" + r2 + "\\)?" and // The same as above, possibly with a `?` after it r4 = r3 + "\\??" From b49b8ff6bd1c0cd3a9a916f62d447238cec3cbb7 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> Date: Wed, 13 May 2026 13:47:53 +0100 Subject: [PATCH 8/8] Give slightly more detail in change note --- .../lib/change-notes/2026-05-12-improved-alphanumeric-regex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md b/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md index b87890a70ac0..df3aaf3613fa 100644 --- a/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md +++ b/actions/ql/lib/change-notes/2026-05-12-improved-alphanumeric-regex.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. \ No newline at end of file +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. \ No newline at end of file