diff --git a/ql/lib/codeql/actions/dataflow/FlowSources.qll b/ql/lib/codeql/actions/dataflow/FlowSources.qll index 580fb1d25ab..db111b9e190 100644 --- a/ql/lib/codeql/actions/dataflow/FlowSources.qll +++ b/ql/lib/codeql/actions/dataflow/FlowSources.qll @@ -252,10 +252,25 @@ class CompositeActionInputSource extends RemoteFlowSource { } /** - * A downloadeded artifact. + * A downloaded artifact. */ private class ArtifactSource extends RemoteFlowSource { ArtifactSource() { this.asExpr() instanceof UntrustedArtifactDownloadStep } override string getSourceType() { result = "artifact" } } + +/** + * A list of file names returned by dorny/paths-filter. + */ +private class DornyPathsFilterSource extends RemoteFlowSource { + DornyPathsFilterSource() { + exists(UsesStep u | + u.getCallee() = "dorny/paths-filter" and + u.getArgument("list-files") = ["csv", "json"] and + this.asExpr() = u + ) + } + + override string getSourceType() { result = "filename" } +} diff --git a/ql/lib/codeql/actions/dataflow/FlowSteps.qll b/ql/lib/codeql/actions/dataflow/FlowSteps.qll index b24f9484a80..32c329d8c67 100644 --- a/ql/lib/codeql/actions/dataflow/FlowSteps.qll +++ b/ql/lib/codeql/actions/dataflow/FlowSteps.qll @@ -124,3 +124,23 @@ class ArtifactDownloadToUseTaintStep extends AdditionalTaintStep { artifactDownloadToUseStep(node1, node2) } } + +/** + * A read of the _files field of the dorny/paths-filter action. + */ +predicate dornyPathsFilterTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(UsesStep u, StepsExpression o | + u.getCallee() = "dorny/paths-filter" and + u.getArgument("list-files") = ["csv", "json"] and + u = pred.asExpr() and + o.getStepId() = u.getId() and + o.getFieldName().matches("%_files") and + succ.asExpr() = o + ) +} + +class DornyPathsFilterTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + dornyPathsFilterTaintStep(node1, node2) + } +} diff --git a/ql/lib/ext/dorny_paths-filter.model.yml b/ql/lib/ext/dorny_paths-filter.model.yml deleted file mode 100644 index 79621a6a30c..00000000000 --- a/ql/lib/ext/dorny_paths-filter.model.yml +++ /dev/null @@ -1,6 +0,0 @@ -extensions: - - addsTo: - pack: githubsecuritylab/actions-all - extensible: sourceModel - data: - - ["dorny/paths-filter", "*", "output.changes", "filename", "manual"] diff --git a/ql/test/library-tests/test.expected b/ql/test/library-tests/test.expected index 5bd009b31b0..d7f944c5a3d 100644 --- a/ql/test/library-tests/test.expected +++ b/ql/test/library-tests/test.expected @@ -438,7 +438,6 @@ sources | amannn/action-semantic-pull-request | * | output.error_message | text | manual | | cypress-io/github-action | * | env.GH_BRANCH | branch | manual | | dawidd6/action-download-artifact | * | output.artifacts | artifact | manual | -| dorny/paths-filter | * | output.changes | filename | manual | | franzdiebold/github-env-vars-action | * | output.CI_PR_DESCRIPTION | text | manual | | franzdiebold/github-env-vars-action | * | output.CI_PR_TITLE | title | manual | | googlecloudplatform/magic-modules | * | output.changed-files | filename | manual | diff --git a/ql/test/query-tests/Security/CWE-094/.github/workflows/test2.yml b/ql/test/query-tests/Security/CWE-094/.github/workflows/test2.yml new file mode 100644 index 00000000000..03ee63fe9cf --- /dev/null +++ b/ql/test/query-tests/Security/CWE-094/.github/workflows/test2.yml @@ -0,0 +1,64 @@ +name: List files + +on: + pull_request_target: + types: [ opened, synchronize, workflow_dispatch] + +permissions: {} +jobs: + test: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + steps: + - name: Check for relevant changes + uses: dorny/paths-filter@v3 + id: changed + with: + list-files: json + filters: | + locale: + - '*.xml' + - name: Changed files 1 + run: | + echo changed: ${{ steps.changed.outputs.locale_files }} + echo changed: ${{ steps.changed.outputs.changes }} + - name: Check for relevant changes + uses: dorny/paths-filter@v3 + id: changed2 + with: + list-files: csv + filters: | + locale: + - '*.xml' + - name: Changed files 2 + run: | + echo changed:${{ steps.changed2.outputs.locale_files }} + echo changed: ${{ steps.changed2.outputs.changes }} + - name: Check for relevant changes + uses: dorny/paths-filter@v3 + id: changed3 + with: + list-files: shell + filters: | + locale: + - '*.xml' + - name: Changed files 3 + run: | + echo changed:${{ steps.changed3.outputs.locale_files }} + echo changed: ${{ steps.changed3.outputs.changes }} + - name: Check for relevant changes + uses: dorny/paths-filter@v3 + id: changed4 + with: + list-files: escape + filters: | + locale: + - '*.xml' + - name: Changed files 4 + run: | + echo changed:${{ steps.changed4.outputs.locale_files }} + echo changed: ${{ steps.changed4.outputs.changes }} diff --git a/ql/test/query-tests/Security/CWE-094/CodeInjection.expected b/ql/test/query-tests/Security/CWE-094/CodeInjection.expected index 50cb0c40d24..e220d368b20 100644 --- a/ql/test/query-tests/Security/CWE-094/CodeInjection.expected +++ b/ql/test/query-tests/Security/CWE-094/CodeInjection.expected @@ -55,6 +55,8 @@ edges | .github/workflows/simple2.yml:18:9:26:6 | Uses Step: step [value] | .github/workflows/simple2.yml:29:24:29:54 | steps.step.outputs.value | | .github/workflows/simple2.yml:22:20:22:64 | steps.source.outputs.all_changed_files | .github/workflows/simple2.yml:18:9:26:6 | Uses Step: step [value] | | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | +| .github/workflows/test2.yml:17:9:25:6 | Uses Step: changed | .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | +| .github/workflows/test2.yml:29:9:37:6 | Uses Step: changed2 | .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | .github/workflows/test.yml:49:20:49:56 | needs.job1.outputs['job_output'] | | .github/workflows/test.yml:8:20:8:50 | steps.step5.outputs.MSG5 | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | | .github/workflows/test.yml:12:9:18:6 | Uses Step: step0 [value] | .github/workflows/test.yml:20:18:20:48 | steps.step0.outputs.value | @@ -210,6 +212,10 @@ nodes | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | semmle.label | env.ISSUE_KEY | +| .github/workflows/test2.yml:17:9:25:6 | Uses Step: changed | semmle.label | Uses Step: changed | +| .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | semmle.label | steps.changed.outputs.locale_files | +| .github/workflows/test2.yml:29:9:37:6 | Uses Step: changed2 | semmle.label | Uses Step: changed2 | +| .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | semmle.label | steps.changed2.outputs.locale_files | | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | semmle.label | Job outputs node [job_output] | | .github/workflows/test.yml:8:20:8:50 | steps.step5.outputs.MSG5 | semmle.label | steps.step5.outputs.MSG5 | | .github/workflows/test.yml:12:9:18:6 | Uses Step: step0 [value] | semmle.label | Uses Step: step0 [value] | diff --git a/ql/test/query-tests/Security/CWE-094/PrivilegedCodeInjection.expected b/ql/test/query-tests/Security/CWE-094/PrivilegedCodeInjection.expected index 9068ef92715..1c4ab8a61cf 100644 --- a/ql/test/query-tests/Security/CWE-094/PrivilegedCodeInjection.expected +++ b/ql/test/query-tests/Security/CWE-094/PrivilegedCodeInjection.expected @@ -55,6 +55,8 @@ edges | .github/workflows/simple2.yml:18:9:26:6 | Uses Step: step [value] | .github/workflows/simple2.yml:29:24:29:54 | steps.step.outputs.value | | .github/workflows/simple2.yml:22:20:22:64 | steps.source.outputs.all_changed_files | .github/workflows/simple2.yml:18:9:26:6 | Uses Step: step [value] | | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | +| .github/workflows/test2.yml:17:9:25:6 | Uses Step: changed | .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | +| .github/workflows/test2.yml:29:9:37:6 | Uses Step: changed2 | .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | .github/workflows/test.yml:49:20:49:56 | needs.job1.outputs['job_output'] | | .github/workflows/test.yml:8:20:8:50 | steps.step5.outputs.MSG5 | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | | .github/workflows/test.yml:12:9:18:6 | Uses Step: step0 [value] | .github/workflows/test.yml:20:18:20:48 | steps.step0.outputs.value | @@ -210,6 +212,10 @@ nodes | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | semmle.label | env.ISSUE_KEY | +| .github/workflows/test2.yml:17:9:25:6 | Uses Step: changed | semmle.label | Uses Step: changed | +| .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | semmle.label | steps.changed.outputs.locale_files | +| .github/workflows/test2.yml:29:9:37:6 | Uses Step: changed2 | semmle.label | Uses Step: changed2 | +| .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | semmle.label | steps.changed2.outputs.locale_files | | .github/workflows/test.yml:8:7:10:4 | Job outputs node [job_output] | semmle.label | Job outputs node [job_output] | | .github/workflows/test.yml:8:20:8:50 | steps.step5.outputs.MSG5 | semmle.label | steps.step5.outputs.MSG5 | | .github/workflows/test.yml:12:9:18:6 | Uses Step: step0 [value] | semmle.label | Uses Step: step0 [value] | @@ -319,6 +325,8 @@ subpaths | .github/workflows/simple3.yml:22:11:22:37 | toJSON(github.event) | .github/workflows/simple3.yml:22:11:22:37 | toJSON(github.event) | .github/workflows/simple3.yml:22:11:22:37 | toJSON(github.event) | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/simple3.yml:22:11:22:37 | toJSON(github.event) | ${{ toJSON(github.event) }} | | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | ${{ github.event.pull_request.title }} | | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/test1.yml:25:20:25:39 | env.ISSUE_KEY | ${{ env.ISSUE_KEY }} | +| .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | .github/workflows/test2.yml:17:9:25:6 | Uses Step: changed | .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/test2.yml:27:26:27:66 | steps.changed.outputs.locale_files | ${{ steps.changed.outputs.locale_files }} | +| .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | .github/workflows/test2.yml:29:9:37:6 | Uses Step: changed2 | .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/test2.yml:39:25:39:66 | steps.changed2.outputs.locale_files | ${{ steps.changed2.outputs.locale_files }} | | .github/workflows/test.yml:49:20:49:56 | needs.job1.outputs['job_output'] | .github/workflows/test.yml:15:20:15:64 | github.event['head_commit']['message'] | .github/workflows/test.yml:49:20:49:56 | needs.job1.outputs['job_output'] | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/test.yml:49:20:49:56 | needs.job1.outputs['job_output'] | ${{needs.job1.outputs['job_output']}} | | .github/workflows/workflow_run.yml:9:19:9:64 | github.event.workflow_run.display_title | .github/workflows/workflow_run.yml:9:19:9:64 | github.event.workflow_run.display_title | .github/workflows/workflow_run.yml:9:19:9:64 | github.event.workflow_run.display_title | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/workflow_run.yml:9:19:9:64 | github.event.workflow_run.display_title | ${{ github.event.workflow_run.display_title }} | | .github/workflows/workflow_run.yml:10:19:10:70 | github.event.workflow_run.head_commit.message | .github/workflows/workflow_run.yml:10:19:10:70 | github.event.workflow_run.head_commit.message | .github/workflows/workflow_run.yml:10:19:10:70 | github.event.workflow_run.head_commit.message | Potential privileged code injection in $@, which may be controlled by an external user. | .github/workflows/workflow_run.yml:10:19:10:70 | github.event.workflow_run.head_commit.message | ${{ github.event.workflow_run.head_commit.message }} |