From e45010ec5bdcbff5f5136b85cb75ae1a7d1e8e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Fri, 12 Apr 2024 13:07:54 +0200 Subject: [PATCH] Add Secret exfiltration query --- .../security/SecretExfiltrationQuery.qll | 22 ++++++++ ...rSource_sonarcloud-github-action.model.yml | 7 +++ ql/src/Security/CWE-200/SecretExfiltration.ql | 22 ++++++++ .../CWE-200/.github/workflows/test1.yml | 50 +++++++++++++++++++ .../CWE-200/SecretExfiltration.expected | 22 ++++++++ .../Security/CWE-200/SecretExfiltration.qlref | 2 + 6 files changed, 125 insertions(+) create mode 100644 ql/lib/codeql/actions/security/SecretExfiltrationQuery.qll create mode 100644 ql/lib/ext/SonarSource_sonarcloud-github-action.model.yml create mode 100644 ql/src/Security/CWE-200/SecretExfiltration.ql create mode 100644 ql/test/query-tests/Security/CWE-200/.github/workflows/test1.yml create mode 100644 ql/test/query-tests/Security/CWE-200/SecretExfiltration.expected create mode 100644 ql/test/query-tests/Security/CWE-200/SecretExfiltration.qlref diff --git a/ql/lib/codeql/actions/security/SecretExfiltrationQuery.qll b/ql/lib/codeql/actions/security/SecretExfiltrationQuery.qll new file mode 100644 index 00000000000..1886af435cf --- /dev/null +++ b/ql/lib/codeql/actions/security/SecretExfiltrationQuery.qll @@ -0,0 +1,22 @@ +private import actions +private import codeql.actions.TaintTracking +private import codeql.actions.dataflow.ExternalFlow +import codeql.actions.dataflow.FlowSources +private import codeql.actions.security.ArtifactPoisoningQuery +import codeql.actions.DataFlow + +private class SecretExfiltrationSink extends DataFlow::Node { + SecretExfiltrationSink() { externallyDefinedSink(this, "secret-exfiltration") } +} + +/** + * A taint-tracking configuration for untrusted data that reaches a sink where it may lead to secret exfiltration + */ +private module SecretExfiltrationConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + predicate isSink(DataFlow::Node sink) { sink instanceof SecretExfiltrationSink } +} + +/** Tracks flow of unsafe user input that is used in a context where it may lead to a secret exfiltration. */ +module SecretExfiltrationFlow = TaintTracking::Global; diff --git a/ql/lib/ext/SonarSource_sonarcloud-github-action.model.yml b/ql/lib/ext/SonarSource_sonarcloud-github-action.model.yml new file mode 100644 index 00000000000..0220f0d54d8 --- /dev/null +++ b/ql/lib/ext/SonarSource_sonarcloud-github-action.model.yml @@ -0,0 +1,7 @@ +extensions: + - addsTo: + pack: githubsecuritylab/actions-all + extensible: sinkModel + data: + - ["SonarSource/sonarcloud-github-action", "*", "input.args", "secret-exfiltration", "manual"] + diff --git a/ql/src/Security/CWE-200/SecretExfiltration.ql b/ql/src/Security/CWE-200/SecretExfiltration.ql new file mode 100644 index 00000000000..a6d1c18b733 --- /dev/null +++ b/ql/src/Security/CWE-200/SecretExfiltration.ql @@ -0,0 +1,22 @@ +/** + * @name Secret exfiltration + * @description Secrets may be exfiltrated by an attacker who can control the data sent to an external service. + * @kind path-problem + * @problem.severity error + * @security-severity 9.0 + * @precision high + * @id actions/secret-exfiltration + * @tags actions + * security + * external/cwe/cwe-200 + */ + +import actions +import codeql.actions.security.SecretExfiltrationQuery +import SecretExfiltrationFlow::PathGraph + +from SecretExfiltrationFlow::PathNode source, SecretExfiltrationFlow::PathNode sink +where SecretExfiltrationFlow::flowPath(source, sink) +select sink.getNode(), source, sink, + "Potential secret exfiltration in $@, which may be be leaked to an attacker-controlled resource.", + sink, sink.getNode().asExpr().(Expression).getRawExpression() diff --git a/ql/test/query-tests/Security/CWE-200/.github/workflows/test1.yml b/ql/test/query-tests/Security/CWE-200/.github/workflows/test1.yml new file mode 100644 index 00000000000..21e7aac4768 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-200/.github/workflows/test1.yml @@ -0,0 +1,50 @@ +name: Sonar Code Coverage Upload +on: + workflow_run: + workflows: ["Build/Test"] + types: [completed] +jobs: + sonar: + name: Sonar + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + steps: + - name: 'Download code coverage' + uses: actions/github-script@v7 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "oc-code-coverage" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/oc-code-coverage.zip`, Buffer.from(download.data)); + - name: 'Unzip code coverage' + run: unzip oc-code-coverage.zip -d coverage + - name: set env vars + run: | + echo "SONAR_PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV + echo "SONAR_BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV + echo "SONAR_HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV + - name: SonarCloud Scan (PR) + uses: sonarsource/sonarcloud-github-action@master + if: env.SONAR_HEAD != 'develop' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.scm.revision=${{ github.event.workflow_run.head_sha }} + -Dsonar.pullrequest.key=${{ env.SONAR_PR_NUM }} + -Dsonar.pullrequest.branch=${{ env.SONAR_HEAD }} + -Dsonar.pullrequest.base=${{ env.SONAR_BASE }} diff --git a/ql/test/query-tests/Security/CWE-200/SecretExfiltration.expected b/ql/test/query-tests/Security/CWE-200/SecretExfiltration.expected new file mode 100644 index 00000000000..3fbc081a0f4 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-200/SecretExfiltration.expected @@ -0,0 +1,22 @@ +edges +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_BASE] | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_BASE | +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_HEAD] | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_HEAD | +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_PR_NUM] | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_PR_NUM | +| .github/workflows/test1.yml:12:9:32:6 | Uses Step | .github/workflows/test1.yml:34:9:39:6 | Run Step | +| .github/workflows/test1.yml:34:9:39:6 | Run Step | .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_BASE] | +| .github/workflows/test1.yml:34:9:39:6 | Run Step | .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_HEAD] | +| .github/workflows/test1.yml:34:9:39:6 | Run Step | .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_PR_NUM] | +nodes +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_BASE] | semmle.label | Job: sonar [SONAR_BASE] | +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_HEAD] | semmle.label | Job: sonar [SONAR_HEAD] | +| .github/workflows/test1.yml:8:5:50:59 | Job: sonar [SONAR_PR_NUM] | semmle.label | Job: sonar [SONAR_PR_NUM] | +| .github/workflows/test1.yml:12:9:32:6 | Uses Step | semmle.label | Uses Step | +| .github/workflows/test1.yml:34:9:39:6 | Run Step | semmle.label | Run Step | +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_BASE | semmle.label | env.SONAR_BASE | +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_HEAD | semmle.label | env.SONAR_HEAD | +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_PR_NUM | semmle.label | env.SONAR_PR_NUM | +subpaths +#select +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_BASE | .github/workflows/test1.yml:12:9:32:6 | Uses Step | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_BASE | Potential secret exfiltration in $@, which may be be leaked to an attacker-controlled resource. | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_BASE | ${{ env.SONAR_BASE }} | +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_HEAD | .github/workflows/test1.yml:12:9:32:6 | Uses Step | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_HEAD | Potential secret exfiltration in $@, which may be be leaked to an attacker-controlled resource. | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_HEAD | ${{ env.SONAR_HEAD }} | +| .github/workflows/test1.yml:47:11:50:59 | env.SONAR_PR_NUM | .github/workflows/test1.yml:12:9:32:6 | Uses Step | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_PR_NUM | Potential secret exfiltration in $@, which may be be leaked to an attacker-controlled resource. | .github/workflows/test1.yml:47:11:50:59 | env.SONAR_PR_NUM | ${{ env.SONAR_PR_NUM }} | diff --git a/ql/test/query-tests/Security/CWE-200/SecretExfiltration.qlref b/ql/test/query-tests/Security/CWE-200/SecretExfiltration.qlref new file mode 100644 index 00000000000..cd179c0f1e6 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-200/SecretExfiltration.qlref @@ -0,0 +1,2 @@ +Security/CWE-200/SecretExfiltration.ql +