From e2acd1b6684c4f962604f73602bf74c498fd4370 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 28 Nov 2025 23:15:36 +0000 Subject: [PATCH 1/5] Add test with push and workflow_dispatch triggers This is based on push.yml, and it should still be found by actions/code-injection/medium, but it isn't. --- .../workflows/push_and_workflow_dispatch.yml | 18 ++++++++++++++++++ .../CWE-094/CodeInjectionCritical.expected | 10 ++++++++++ .../CWE-094/CodeInjectionMedium.expected | 10 ++++++++++ 3 files changed, 38 insertions(+) create mode 100644 actions/ql/test/query-tests/Security/CWE-094/.github/workflows/push_and_workflow_dispatch.yml diff --git a/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/push_and_workflow_dispatch.yml b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/push_and_workflow_dispatch.yml new file mode 100644 index 000000000000..8b7a6df009c2 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/push_and_workflow_dispatch.yml @@ -0,0 +1,18 @@ +on: + push: + workflow_dispatch: + +jobs: + echo-chamber: + runs-on: ubuntu-latest + steps: + - run: echo '${{ github.event.commits[11].message }}' + - run: echo '${{ github.event.commits[11].author.email }}' + - run: echo '${{ github.event.commits[11].author.name }}' + - run: echo '${{ github.event.head_commit.message }}' + - run: echo '${{ github.event.head_commit.author.email }}' + - run: echo '${{ github.event.head_commit.author.name }}' + - run: echo '${{ github.event.head_commit.committer.email }}' + - run: echo '${{ github.event.head_commit.committer.name }}' + - run: echo '${{ github.event.commits[11].committer.email }}' + - run: echo '${{ github.event.commits[11].committer.name }}' \ 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 af788f6280b2..9bf7e9aa56db 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected @@ -435,6 +435,16 @@ nodes | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | semmle.label | github.event.head_commit.committer.name | | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | semmle.label | github.event.commits[11].committer.email | | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | semmle.label | github.event.commits[11].committer.name | +| .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | semmle.label | github.event.commits[11].message | +| .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | semmle.label | github.event.commits[11].author.email | +| .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | semmle.label | github.event.commits[11].author.name | +| .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | semmle.label | github.event.head_commit.message | +| .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | semmle.label | github.event.head_commit.author.email | +| .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | semmle.label | github.event.head_commit.author.name | +| .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | semmle.label | github.event.head_commit.committer.email | +| .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | semmle.label | github.event.head_commit.committer.name | +| .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | semmle.label | github.event.commits[11].committer.email | +| .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | semmle.label | github.event.commits[11].committer.name | | .github/workflows/reusable-workflow-1.yml:6:7:6:11 | input taint | semmle.label | input taint | | .github/workflows/reusable-workflow-1.yml:36:21:36:39 | inputs.taint | semmle.label | inputs.taint | | .github/workflows/reusable-workflow-1.yml:44:19:44:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | 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 6c948d9559ea..c03704e02638 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected @@ -435,6 +435,16 @@ nodes | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | semmle.label | github.event.head_commit.committer.name | | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | semmle.label | github.event.commits[11].committer.email | | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | semmle.label | github.event.commits[11].committer.name | +| .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | semmle.label | github.event.commits[11].message | +| .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | semmle.label | github.event.commits[11].author.email | +| .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | semmle.label | github.event.commits[11].author.name | +| .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | semmle.label | github.event.head_commit.message | +| .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | semmle.label | github.event.head_commit.author.email | +| .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | semmle.label | github.event.head_commit.author.name | +| .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | semmle.label | github.event.head_commit.committer.email | +| .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | semmle.label | github.event.head_commit.committer.name | +| .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | semmle.label | github.event.commits[11].committer.email | +| .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | semmle.label | github.event.commits[11].committer.name | | .github/workflows/reusable-workflow-1.yml:6:7:6:11 | input taint | semmle.label | input taint | | .github/workflows/reusable-workflow-1.yml:36:21:36:39 | inputs.taint | semmle.label | inputs.taint | | .github/workflows/reusable-workflow-1.yml:44:19:44:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | From f6bdb3a126e84a0eb2388cac704c0a40e4a24882 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 28 Nov 2025 23:17:35 +0000 Subject: [PATCH 2/5] Fix filtering of code injection alerts between medium and critical --- .../actions/security/CodeInjectionQuery.qll | 22 +++++++++++++++++++ .../Security/CWE-094/CodeInjectionCritical.ql | 3 +-- .../Security/CWE-094/CodeInjectionMedium.ql | 8 +------ .../CWE-094/CodeInjectionMedium.expected | 14 ++++++++++++ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll index 0f77acc2444b..8bffbbde5d1d 100644 --- a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll +++ b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll @@ -91,3 +91,25 @@ private module CodeInjectionConfig implements DataFlow::ConfigSig { /** Tracks flow of unsafe user input that is used to construct and evaluate a code script. */ module CodeInjectionFlow = TaintTracking::Global; + +/** + * Holds if the flow from `source` to `sink` has critical severity and they are + * linked by `event`. + */ +pragma[inline] +predicate criticalSeverity(DataFlow::Node source, DataFlow::Node sink, Event event) { + event = getRelevantCriticalEventForSink(sink) and + source.(RemoteFlowSource).getEventName() = event.getName() +} + +/** Holds if the flow from `source` to `sink` has medium severity. */ +pragma[inline] +predicate mediumSeverity(DataFlow::Node source, DataFlow::Node sink) { + not criticalSeverity(source, sink, _) and + // exclude cases where the sink is a JS script and the expression uses toJson + not exists(UsesStep script | + script.getCallee() = "actions/github-script" and + script.getArgumentExpr("script") = sink.asExpr() and + exists(getAToJsonReferenceExpression(sink.asExpr().(Expression).getExpression(), _)) + ) +} diff --git a/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql b/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql index ed30e4da71c8..b38332798c26 100644 --- a/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql +++ b/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql @@ -22,8 +22,7 @@ import codeql.actions.security.ControlChecks from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, Event event where CodeInjectionFlow::flowPath(source, sink) and - event = getRelevantCriticalEventForSink(sink.getNode()) and - source.getNode().(RemoteFlowSource).getEventName() = event.getName() + criticalSeverity(source.getNode(), sink.getNode(), event) select sink.getNode(), source, sink, "Potential code injection in $@, which may be controlled by an external user ($@).", sink, sink.getNode().asExpr().(Expression).getRawExpression(), event, event.getName() diff --git a/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql b/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql index 0f8b6e13a290..03bbeb962ce7 100644 --- a/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql +++ b/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql @@ -21,13 +21,7 @@ import CodeInjectionFlow::PathGraph from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink where CodeInjectionFlow::flowPath(source, sink) and - inNonPrivilegedContext(sink.getNode().asExpr()) and - // exclude cases where the sink is a JS script and the expression uses toJson - not exists(UsesStep script | - script.getCallee() = "actions/github-script" and - script.getArgumentExpr("script") = sink.getNode().asExpr() and - exists(getAToJsonReferenceExpression(sink.getNode().asExpr().(Expression).getExpression(), _)) - ) + mediumSeverity(source.getNode(), sink.getNode()) select sink.getNode(), source, sink, "Potential code injection in $@, which may be controlled by an external user.", sink, sink.getNode().asExpr().(Expression).getRawExpression() 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 c03704e02638..4bbe7da0aaf3 100644 --- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected +++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected @@ -729,6 +729,16 @@ subpaths | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:14:19:14:64 | github.event.head_commit.committer.name | ${{ github.event.head_commit.committer.name }} | | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:15:19:15:65 | github.event.commits[11].committer.email | ${{ github.event.commits[11].committer.email }} | | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push.yml:16:19:16:64 | github.event.commits[11].committer.name | ${{ github.event.commits[11].committer.name }} | +| .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:9:19:9:57 | github.event.commits[11].message | ${{ github.event.commits[11].message }} | +| .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:10:19:10:62 | github.event.commits[11].author.email | ${{ github.event.commits[11].author.email }} | +| .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:11:19:11:61 | github.event.commits[11].author.name | ${{ github.event.commits[11].author.name }} | +| .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:12:19:12:57 | github.event.head_commit.message | ${{ github.event.head_commit.message }} | +| .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:13:19:13:62 | github.event.head_commit.author.email | ${{ github.event.head_commit.author.email }} | +| .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:14:19:14:61 | github.event.head_commit.author.name | ${{ github.event.head_commit.author.name }} | +| .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:15:19:15:65 | github.event.head_commit.committer.email | ${{ github.event.head_commit.committer.email }} | +| .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:16:19:16:64 | github.event.head_commit.committer.name | ${{ github.event.head_commit.committer.name }} | +| .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:17:19:17:65 | github.event.commits[11].committer.email | ${{ github.event.commits[11].committer.email }} | +| .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/push_and_workflow_dispatch.yml:18:19:18:64 | github.event.commits[11].committer.name | ${{ github.event.commits[11].committer.name }} | | .github/workflows/reusable-workflow-1.yml:36:21:36:39 | inputs.taint | .github/workflows/reusable-workflow-caller-1.yml:11:15:11:52 | github.event.pull_request.title | .github/workflows/reusable-workflow-1.yml:36:21:36:39 | inputs.taint | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/reusable-workflow-1.yml:36:21:36:39 | inputs.taint | ${{ inputs.taint }} | | .github/workflows/reusable-workflow-1.yml:53:26:53:39 | env.log | .github/workflows/reusable-workflow-1.yml:44:19:44:56 | github.event.pull_request.title | .github/workflows/reusable-workflow-1.yml:53:26:53:39 | env.log | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/reusable-workflow-1.yml:53:26:53:39 | env.log | ${{ env.log }} | | .github/workflows/reusable-workflow-1.yml:66:34:66:52 | env.prev_log | .github/workflows/reusable-workflow-1.yml:45:24:45:61 | github.event.changes.title.from | .github/workflows/reusable-workflow-1.yml:66:34:66:52 | env.prev_log | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/reusable-workflow-1.yml:66:34:66:52 | env.prev_log | ${{ env.prev_log }} | @@ -739,6 +749,10 @@ subpaths | .github/workflows/test10.yml:333:34:333:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:333:34:333:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:333:34:333:77 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test10.yml:333:34:333:77 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | | .github/workflows/test10.yml:423:34:423:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:423:34:423:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:423:34:423:77 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test10.yml:423:34:423:77 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | | .github/workflows/test10.yml:518:34:518:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:518:34:518:77 | github.event.workflow_run.head_branch | .github/workflows/test10.yml:518:34:518:77 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test10.yml:518:34:518:77 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | +| .github/workflows/test20.yml:15:54:15:94 | github.event.pull_request.head.ref | .github/workflows/test20.yml:15:54:15:94 | github.event.pull_request.head.ref | .github/workflows/test20.yml:15:54:15:94 | github.event.pull_request.head.ref | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test20.yml:15:54:15:94 | github.event.pull_request.head.ref | ${{ github.event.pull_request.head.ref }} | +| .github/workflows/test21.yml:22:35:22:73 | github.event.head_commit.message | .github/workflows/test21.yml:22:35:22:73 | github.event.head_commit.message | .github/workflows/test21.yml:22:35:22:73 | github.event.head_commit.message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test21.yml:22:35:22:73 | github.event.head_commit.message | ${{ github.event.head_commit.message }} | +| .github/workflows/test21.yml:23:36:23:74 | github.event.head_commit.message | .github/workflows/test21.yml:23:36:23:74 | github.event.head_commit.message | .github/workflows/test21.yml:23:36:23:74 | github.event.head_commit.message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test21.yml:23:36:23:74 | github.event.head_commit.message | ${{ github.event.head_commit.message }} | +| .github/workflows/test21.yml:24:50:24:88 | github.event.head_commit.message | .github/workflows/test21.yml:24:50:24:88 | github.event.head_commit.message | .github/workflows/test21.yml:24:50:24:88 | github.event.head_commit.message | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/test21.yml:24:50:24:88 | github.event.head_commit.message | ${{ github.event.head_commit.message }} | | .github/workflows/workflow_run_branches1.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches1.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches1.yml:13:20:13:63 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/workflow_run_branches1.yml:13:20:13:63 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | | .github/workflows/workflow_run_branches2.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches2.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches2.yml:13:20:13:63 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/workflow_run_branches2.yml:13:20:13:63 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | | .github/workflows/workflow_run_branches4.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches4.yml:13:20:13:63 | github.event.workflow_run.head_branch | .github/workflows/workflow_run_branches4.yml:13:20:13:63 | github.event.workflow_run.head_branch | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/workflow_run_branches4.yml:13:20:13:63 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | From 8bac1dec83220ae9c59d1355195bfb3928f736b2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Sat, 29 Nov 2025 01:06:00 +0000 Subject: [PATCH 3/5] Add change note --- .../2025-11-28-fix-code-injection-alert-filtering.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 actions/ql/lib/change-notes/2025-11-28-fix-code-injection-alert-filtering.md diff --git a/actions/ql/lib/change-notes/2025-11-28-fix-code-injection-alert-filtering.md b/actions/ql/lib/change-notes/2025-11-28-fix-code-injection-alert-filtering.md new file mode 100644 index 000000000000..b80ef77c2bcc --- /dev/null +++ b/actions/ql/lib/change-notes/2025-11-28-fix-code-injection-alert-filtering.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* The query `actions/code-injection/medium` has been updated to include results which were incorrectly excluded while filtering out results that are reported by `actions/code-injection/critical`. From fb841ea5915eefbc98a5ad707f8c64fd0249cf33 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 3 Dec 2025 11:27:35 +0000 Subject: [PATCH 4/5] Make predicates containing query logic more self-contained --- .../actions/security/CodeInjectionQuery.qll | 30 +++++++++++-------- .../Security/CWE-094/CodeInjectionCritical.ql | 4 +-- .../Security/CWE-094/CodeInjectionMedium.ql | 4 +-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll index 8bffbbde5d1d..bdea8e81962d 100644 --- a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll +++ b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll @@ -93,23 +93,29 @@ private module CodeInjectionConfig implements DataFlow::ConfigSig { module CodeInjectionFlow = TaintTracking::Global; /** - * Holds if the flow from `source` to `sink` has critical severity and they are - * linked by `event`. + * Holds if there is a code injection flow from `source` to `sink` with + * critical severity, linked by `event`. */ -pragma[inline] -predicate criticalSeverity(DataFlow::Node source, DataFlow::Node sink, Event event) { - event = getRelevantCriticalEventForSink(sink) and - source.(RemoteFlowSource).getEventName() = event.getName() +predicate criticalSeverityCodeInjection( + CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, Event event +) { + CodeInjectionFlow::flowPath(source, sink) and + event = getRelevantCriticalEventForSink(sink.getNode()) and + source.getNode().(RemoteFlowSource).getEventName() = event.getName() } -/** Holds if the flow from `source` to `sink` has medium severity. */ -pragma[inline] -predicate mediumSeverity(DataFlow::Node source, DataFlow::Node sink) { - not criticalSeverity(source, sink, _) and +/** + * Holds if there is a code injection flow from `source` to `sink` with medium severity. + */ +predicate mediumSeverityCodeInjection( + CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink +) { + CodeInjectionFlow::flowPath(source, sink) and + not criticalSeverityCodeInjection(source, sink, _) and // exclude cases where the sink is a JS script and the expression uses toJson not exists(UsesStep script | script.getCallee() = "actions/github-script" and - script.getArgumentExpr("script") = sink.asExpr() and - exists(getAToJsonReferenceExpression(sink.asExpr().(Expression).getExpression(), _)) + script.getArgumentExpr("script") = sink.getNode().asExpr() and + exists(getAToJsonReferenceExpression(sink.getNode().asExpr().(Expression).getExpression(), _)) ) } diff --git a/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql b/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql index b38332798c26..001aadd66cb4 100644 --- a/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql +++ b/actions/ql/src/Security/CWE-094/CodeInjectionCritical.ql @@ -20,9 +20,7 @@ import CodeInjectionFlow::PathGraph import codeql.actions.security.ControlChecks from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, Event event -where - CodeInjectionFlow::flowPath(source, sink) and - criticalSeverity(source.getNode(), sink.getNode(), event) +where criticalSeverityCodeInjection(source, sink, event) select sink.getNode(), source, sink, "Potential code injection in $@, which may be controlled by an external user ($@).", sink, sink.getNode().asExpr().(Expression).getRawExpression(), event, event.getName() diff --git a/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql b/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql index 03bbeb962ce7..8bc3fe8f51ad 100644 --- a/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql +++ b/actions/ql/src/Security/CWE-094/CodeInjectionMedium.ql @@ -19,9 +19,7 @@ import codeql.actions.security.CodeInjectionQuery import CodeInjectionFlow::PathGraph from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink -where - CodeInjectionFlow::flowPath(source, sink) and - mediumSeverity(source.getNode(), sink.getNode()) +where mediumSeverityCodeInjection(source, sink) select sink.getNode(), source, sink, "Potential code injection in $@, which may be controlled by an external user.", sink, sink.getNode().asExpr().(Expression).getRawExpression() From 4a16de2bc8abd0bef4ea6169d95b70f655001808 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 3 Dec 2025 12:59:35 +0000 Subject: [PATCH 5/5] Pull out logic into separate predicate --- .../actions/security/CodeInjectionQuery.qll | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll index bdea8e81962d..3d5b8852b850 100644 --- a/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll +++ b/actions/ql/lib/codeql/actions/security/CodeInjectionQuery.qll @@ -19,12 +19,7 @@ class CodeInjectionSink extends DataFlow::Node { Event getRelevantCriticalEventForSink(DataFlow::Node sink) { inPrivilegedContext(sink.asExpr(), result) and not exists(ControlCheck check | check.protects(sink.asExpr(), result, "code-injection")) and - // exclude cases where the sink is a JS script and the expression uses toJson - not exists(UsesStep script | - script.getCallee() = "actions/github-script" and - script.getArgumentExpr("script") = sink.asExpr() and - exists(getAToJsonReferenceExpression(sink.asExpr().(Expression).getExpression(), _)) - ) + not isGithubScriptUsingToJson(sink.asExpr()) } /** @@ -112,10 +107,17 @@ predicate mediumSeverityCodeInjection( ) { CodeInjectionFlow::flowPath(source, sink) and not criticalSeverityCodeInjection(source, sink, _) and - // exclude cases where the sink is a JS script and the expression uses toJson - not exists(UsesStep script | + not isGithubScriptUsingToJson(sink.getNode().asExpr()) +} + +/** + * Holds if `expr` is the `script` input to `actions/github-script` and it uses + * `toJson`. + */ +predicate isGithubScriptUsingToJson(Expression expr) { + exists(UsesStep script | script.getCallee() = "actions/github-script" and - script.getArgumentExpr("script") = sink.getNode().asExpr() and - exists(getAToJsonReferenceExpression(sink.getNode().asExpr().(Expression).getExpression(), _)) + script.getArgumentExpr("script") = expr and + exists(getAToJsonReferenceExpression(expr.getExpression(), _)) ) }