From aa5a74fc442a06bbb2f97757d7f02126612d6184 Mon Sep 17 00:00:00 2001 From: JarLob Date: Sat, 25 Jul 2026 20:55:13 +0300 Subject: [PATCH 1/2] Fix merge_group event source mapping Map merge_group event payloads to their GitHub context so modeled untrusted fields are recognized, and add code-injection regression coverage. --- .../2026-07-27-merge-group-event-source.md | 4 ++++ actions/ql/lib/ext/config/context_event_map.yml | 1 + .../.github/workflows/merge_group_code_injection.yml | 10 ++++++++++ .../Security/CWE-094/CodeInjectionCritical.expected | 1 + .../Security/CWE-094/CodeInjectionMedium.expected | 2 ++ 5 files changed, 18 insertions(+) create mode 100644 actions/ql/lib/change-notes/2026-07-27-merge-group-event-source.md create mode 100644 actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml diff --git a/actions/ql/lib/change-notes/2026-07-27-merge-group-event-source.md b/actions/ql/lib/change-notes/2026-07-27-merge-group-event-source.md new file mode 100644 index 000000000000..41b39b6c452f --- /dev/null +++ b/actions/ql/lib/change-notes/2026-07-27-merge-group-event-source.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* GitHub Actions analysis now recognizes untrusted data in `github.event.merge_group` for workflows triggered by the `merge_group` event. \ No newline at end of file diff --git a/actions/ql/lib/ext/config/context_event_map.yml b/actions/ql/lib/ext/config/context_event_map.yml index 541ac8b9a8f6..311ea7cf5481 100644 --- a/actions/ql/lib/ext/config/context_event_map.yml +++ b/actions/ql/lib/ext/config/context_event_map.yml @@ -19,6 +19,7 @@ extensions: - ["gollum", "github.event.changes"] - ["pull_request_comment", "github.event.comment"] - ["pull_request_comment", "github.event.pull_request"] + - ["merge_group", "github.event.merge_group"] - ["pull_request_comment", "github.head_ref"] - ["pull_request_comment", "github.event.changes"] - ["pull_request_review", "github.event.pull_request"] diff --git a/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml new file mode 100644 index 000000000000..0ee85ed45e8a --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml @@ -0,0 +1,10 @@ +on: + merge_group: + types: [checks_requested] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Use merge group payload + run: echo '${{ toJSON(github.event) }}' \ No newline at end of file diff --git a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected index 9bf7e9aa56db..bf1b6dec7fd5 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected @@ -400,6 +400,7 @@ nodes | .github/workflows/level0.yml:44:20:44:49 | github.event.issue.body | semmle.label | github.event.issue.body | | .github/workflows/level0.yml:69:35:69:66 | github.event.comment.body | semmle.label | github.event.comment.body | | .github/workflows/level1.yml:37:38:37:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | +| .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | semmle.label | toJSON(github.event) | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | | .github/workflows/pull_request_review.yml:7:19:7:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/pull_request_review.yml:8:19:8:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | diff --git a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected index 4bbe7da0aaf3..c3a79774aac3 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected @@ -400,6 +400,7 @@ nodes | .github/workflows/level0.yml:44:20:44:49 | github.event.issue.body | semmle.label | github.event.issue.body | | .github/workflows/level0.yml:69:35:69:66 | github.event.comment.body | semmle.label | github.event.comment.body | | .github/workflows/level1.yml:37:38:37:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | +| .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | semmle.label | toJSON(github.event) | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | | .github/workflows/pull_request_review.yml:7:19:7:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/pull_request_review.yml:8:19:8:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | @@ -718,6 +719,7 @@ subpaths | .github/workflows/inter-job2.yml:45:20:45:53 | needs.job1.outputs.job_output | .github/workflows/inter-job2.yml:22:9:26:6 | Uses Step: source | .github/workflows/inter-job2.yml:45:20:45:53 | needs.job1.outputs.job_output | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/inter-job2.yml:45:20:45:53 | needs.job1.outputs.job_output | ${{needs.job1.outputs.job_output}} | | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | .github/workflows/inter-job4.yml:22:9:26:6 | Uses Step: source | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | ${{needs.job1.outputs.job_output}} | | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | ${{needs.job1.outputs.job_output}} | +| .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | ${{ toJSON(github.event) }} | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | ${{ github.event.commits[11].message }} | | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | ${{ github.event.commits[11].author.email }} | From 147f67964cd9a52b40e6d4d0170e33416f353a68 Mon Sep 17 00:00:00 2001 From: JarLob Date: Mon, 27 Jul 2026 15:03:03 +0300 Subject: [PATCH 2/2] Test direct merge_group event source mapping --- .../CWE-094/.github/workflows/merge_group_code_injection.yml | 4 +++- .../Security/CWE-094/CodeInjectionCritical.expected | 1 + .../query-tests/Security/CWE-094/CodeInjectionMedium.expected | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml index 0ee85ed45e8a..02c9864bb7bf 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml +++ b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/merge_group_code_injection.yml @@ -7,4 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Use merge group payload - run: echo '${{ toJSON(github.event) }}' \ No newline at end of file + run: echo '${{ toJSON(github.event) }}' + - name: Use merge group head ref + run: echo '${{ github.event.merge_group.head_ref }}' \ No newline at end of file diff --git a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected index bf1b6dec7fd5..26f1046ed04f 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected @@ -401,6 +401,7 @@ nodes | .github/workflows/level0.yml:69:35:69:66 | github.event.comment.body | semmle.label | github.event.comment.body | | .github/workflows/level1.yml:37:38:37:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | semmle.label | toJSON(github.event) | +| .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | semmle.label | github.event.merge_group.head_ref | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | | .github/workflows/pull_request_review.yml:7:19:7:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/pull_request_review.yml:8:19:8:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | diff --git a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected index c3a79774aac3..020edd104b3b 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected @@ -401,6 +401,7 @@ nodes | .github/workflows/level0.yml:69:35:69:66 | github.event.comment.body | semmle.label | github.event.comment.body | | .github/workflows/level1.yml:37:38:37:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | semmle.label | toJSON(github.event) | +| .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | semmle.label | github.event.merge_group.head_ref | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | | .github/workflows/pull_request_review.yml:7:19:7:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/pull_request_review.yml:8:19:8:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | @@ -720,6 +721,7 @@ subpaths | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | .github/workflows/inter-job4.yml:22:9:26:6 | Uses Step: source | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/inter-job4.yml:44:20:44:53 | needs.job1.outputs.job_output | ${{needs.job1.outputs.job_output}} | | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/inter-job5.yml:45:20:45:53 | needs.job1.outputs.job_output | ${{needs.job1.outputs.job_output}} | | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/merge_group_code_injection.yml:10:21:10:47 | toJSON(github.event) | ${{ toJSON(github.event) }} | +| .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/merge_group_code_injection.yml:12:21:12:60 | github.event.merge_group.head_ref | ${{ github.event.merge_group.head_ref }} | | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/priv_pull_request.yml:14:21:14:57 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:7:19:7:57 | github.event.commits[11].message | ${{ github.event.commits[11].message }} | | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:8:19:8:62 | github.event.commits[11].author.email | ${{ github.event.commits[11].author.email }} |