mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Improve envvar injection
This commit is contained in:
@@ -34,7 +34,7 @@ module Utils {
|
||||
.regexpReplaceAll("^'", "")
|
||||
.regexpReplaceAll("'$", "") or
|
||||
assignment =
|
||||
line.regexpCapture("(echo|Write-Output)\\s+([^'\"]*)\\s*>>\\s*(\"|')?\\$GITHUB_" +
|
||||
line.regexpCapture("(echo|Write-Output)\\s+(.*)\\s*>>\\s*(\"|')?\\$GITHUB_" +
|
||||
var.toUpperCase() + "(\"|')?", 2)
|
||||
) and
|
||||
key = assignment.splitAt("=", 0).trim() and
|
||||
|
||||
@@ -64,3 +64,24 @@ predicate artifactToOutputStoreStep(DataFlow::Node pred, DataFlow::Node succ, Da
|
||||
value.regexpMatch(["\\$\\(", "`"] + ["cat\\s+", "<"] + ".*" + ["`", "\\)"])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A downloaded artifact that gets assigned to an env var declaration.
|
||||
* - uses: actions/download-artifact@v2
|
||||
* - run: echo "::set-env name=id::$(<pr-id.txt)"
|
||||
*/
|
||||
predicate artifactToEnvStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Run run, string key, string value, ArtifactDownloadStep download |
|
||||
pred.asExpr() = download and
|
||||
succ.asExpr() = run and
|
||||
download.getAFollowingStep() = run and
|
||||
Utils::writeToGitHubEnv(run, key, value) and
|
||||
value.regexpMatch(["\\$\\(", "`"] + ["cat\\s+", "<"] + ".*" + ["`", "\\)"])
|
||||
)
|
||||
}
|
||||
|
||||
class ArtifactDownloadToEnvTaintStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
artifactToEnvStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,38 @@ 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
|
||||
|
||||
predicate writeToGithubEnvSink(DataFlow::Node exprNode, string key, string value) {
|
||||
exists(Expression expr, Run run, string script, string line |
|
||||
script = run.getScript() and
|
||||
line = script.splitAt("\n") and
|
||||
key = line.regexpCapture("echo\\s+(\")?([^=]+)\\s*=(.*)(\")?\\s*>>\\s*\\$GITHUB_ENV", 2) and
|
||||
value = line.regexpCapture("echo\\s+(\")?([^=]+)\\s*=(.*)(\")?\\s*>>\\s*\\$GITHUB_ENV", 3) and
|
||||
expr = exprNode.asExpr() and
|
||||
run.getAnScriptExpr() = expr and
|
||||
value.indexOf(expr.getRawExpression()) > 0
|
||||
)
|
||||
class EnvVarInjectionFromExprSink extends DataFlow::Node {
|
||||
EnvVarInjectionFromExprSink() {
|
||||
exists(Expression expr, Run run, string script, string line, string key, string value |
|
||||
script = run.getScript() and
|
||||
line = script.splitAt("\n") and
|
||||
Utils::extractAssignment(line, "ENV", key, value) and
|
||||
expr = this.asExpr() and
|
||||
run.getAnScriptExpr() = expr and
|
||||
value.indexOf(expr.getRawExpression()) > 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class EnvVarInjectionFromFileSink extends DataFlow::Node {
|
||||
EnvVarInjectionFromFileSink() {
|
||||
exists(Run run, ArtifactDownloadStep step, string value |
|
||||
this.asExpr() = run and
|
||||
step.getAFollowingStep() = run and
|
||||
Utils::writeToGitHubEnv(run, _, value) and
|
||||
// TODO: add support for other commands like `<`, `jq`, ...
|
||||
value.regexpMatch(["\\$\\(", "`"] + ["cat\\s+", "<"] + ".*" + ["`", "\\)"])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class EnvVarInjectionSink extends DataFlow::Node {
|
||||
EnvVarInjectionSink() {
|
||||
writeToGithubEnvSink(this, _, _) or
|
||||
this instanceof EnvVarInjectionFromExprSink or
|
||||
this instanceof EnvVarInjectionFromFileSink or
|
||||
externallyDefinedSink(this, "envvar-injection")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ where
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"Potential environment variable injection in $@, which may be controlled by an external user.",
|
||||
sink, sink.getNode().asExpr().(Expression).getRawExpression()
|
||||
sink, sink.getNode().toString()
|
||||
|
||||
@@ -25,4 +25,4 @@ where
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"Potential privileged environment variable injection in $@, which may be controlled by an external user.",
|
||||
sink, sink.getNode().asExpr().(Expression).getRawExpression()
|
||||
sink, sink.getNode().toString()
|
||||
|
||||
@@ -329,7 +329,9 @@ sources
|
||||
| jitterbit/get-changed-files | * | output.removed | PR changed files |
|
||||
| jitterbit/get-changed-files | * | output.renamed | PR changed files |
|
||||
| khan/pull-request-comment-trigger | * | output.comment_body | Comment body |
|
||||
| marocchino/on_artifact | * | output.* | Downloaded artifact |
|
||||
| octo-org/source-repo/.github/workflows/workflow.yml | * | output.workflow-output | Foo |
|
||||
| redhat-plumbers-in-action/download-artifact | * | output.* | Downloaded artifact |
|
||||
| tj-actions/branch-names | * | output.current_branch | PR current branch |
|
||||
| tj-actions/branch-names | * | output.head_ref_branch | PR head branch |
|
||||
| tj-actions/branch-names | * | output.ref_branch | Branch tirggering workflow run |
|
||||
@@ -426,6 +428,8 @@ testNormalizeExpr
|
||||
| github.event.pull_request.user['login'] | github.event.pull_request.user.login |
|
||||
| github.event.pull_request['user']['login'] | github.event.pull_request.user.login |
|
||||
writeToGitHubEnv
|
||||
| "sha1 | $(<test-results1/sha-number)" |
|
||||
| 'sha2 | $(<test-results2/sha-number)' |
|
||||
| id1 | $(<pr-id1.txt) |
|
||||
| id2 | $(<pr-id2.txt) |
|
||||
| id3 | $(<pr-id3.txt) |
|
||||
@@ -433,6 +437,8 @@ writeToGitHubEnv
|
||||
| sha2 | $(<test-results2/sha-number) |
|
||||
| sha3 | $(<test-results3/sha-number) |
|
||||
writeToGitHubOutput
|
||||
| "sha1 | $(<test-results1/sha-number)" |
|
||||
| 'sha2 | $(<test-results2/sha-number)' |
|
||||
| id1 | $(<pr-id1.txt) |
|
||||
| id2 | $(<pr-id2.txt) |
|
||||
| id3 | $(<pr-id3.txt) |
|
||||
|
||||
75
ql/test/query-tests/Security/CWE-077/.github/workflows/sonar-source.yml
vendored
Normal file
75
ql/test/query-tests/Security/CWE-077/.github/workflows/sonar-source.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
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:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||
ref: ${{ github.event.workflow_run.head_branch }}
|
||||
fetch-depth: 0
|
||||
- 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
|
||||
# on develop branch, only run a baseline scan
|
||||
- name: SonarCloud Scan (Baseline)
|
||||
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.projectKey=opencost_opencost
|
||||
-Dsonar.organization=opencost
|
||||
-Dsonar.branch.name=develop
|
||||
-Dsonar.branch.target=develop
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
print("${{enb.SONAR_PR_NUM}}")
|
||||
- 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 }}
|
||||
-Dsonar.projectKey=opencost_opencost
|
||||
-Dsonar.organization=opencost
|
||||
@@ -1,5 +1,16 @@
|
||||
edges
|
||||
| .github/workflows/sonar-source.yml:17:9:37:6 | Uses Step | .github/workflows/sonar-source.yml:39:9:45:6 | Run Step |
|
||||
| .github/workflows/test2.yml:17:9:47:6 | Uses Step | .github/workflows/test2.yml:47:9:52:6 | Run Step |
|
||||
| .github/workflows/test3.yml:17:7:24:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step |
|
||||
| .github/workflows/test3.yml:24:7:31:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step |
|
||||
nodes
|
||||
| .github/workflows/sonar-source.yml:17:9:37:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/sonar-source.yml:39:9:45:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title |
|
||||
| .github/workflows/test2.yml:17:9:47:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test2.yml:47:9:52:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/test3.yml:17:7:24:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test3.yml:24:7:31:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test3.yml:39:7:44:4 | Run Step | semmle.label | Run Step |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
edges
|
||||
| .github/workflows/sonar-source.yml:17:9:37:6 | Uses Step | .github/workflows/sonar-source.yml:39:9:45:6 | Run Step |
|
||||
| .github/workflows/test2.yml:17:9:47:6 | Uses Step | .github/workflows/test2.yml:47:9:52:6 | Run Step |
|
||||
| .github/workflows/test3.yml:17:7:24:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step |
|
||||
| .github/workflows/test3.yml:24:7:31:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step |
|
||||
nodes
|
||||
| .github/workflows/sonar-source.yml:17:9:37:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/sonar-source.yml:39:9:45:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/test1.yml:22:38:22:75 | github.event.pull_request.title | semmle.label | github.event.pull_request.title |
|
||||
| .github/workflows/test2.yml:17:9:47:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test2.yml:47:9:52:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/test3.yml:17:7:24:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test3.yml:24:7:31:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/test3.yml:39:7:44:4 | Run Step | semmle.label | Run Step |
|
||||
subpaths
|
||||
#select
|
||||
| .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 environment variable 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/sonar-source.yml:39:9:45:6 | Run Step | .github/workflows/sonar-source.yml:17:9:37:6 | Uses Step | .github/workflows/sonar-source.yml:39:9:45:6 | Run Step | Potential privileged environment variable injection in $@, which may be controlled by an external user. | .github/workflows/sonar-source.yml:39:9:45:6 | Run Step | Run Step |
|
||||
| .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 environment variable 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/test2.yml:47:9:52:6 | Run Step | .github/workflows/test2.yml:17:9:47:6 | Uses Step | .github/workflows/test2.yml:47:9:52:6 | Run Step | Potential privileged environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test2.yml:47:9:52:6 | Run Step | Run Step |
|
||||
| .github/workflows/test3.yml:39:7:44:4 | Run Step | .github/workflows/test3.yml:17:7:24:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step | Potential privileged environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test3.yml:39:7:44:4 | Run Step | Run Step |
|
||||
| .github/workflows/test3.yml:39:7:44:4 | Run Step | .github/workflows/test3.yml:24:7:31:4 | Uses Step | .github/workflows/test3.yml:39:7:44:4 | Run Step | Potential privileged environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test3.yml:39:7:44:4 | Run Step | Run Step |
|
||||
|
||||
71
ql/test/query-tests/Security/CWE-094/.github/workflows/sonar-source.yml
vendored
Normal file
71
ql/test/query-tests/Security/CWE-094/.github/workflows/sonar-source.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
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:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||
ref: ${{ github.event.workflow_run.head_branch }}
|
||||
fetch-depth: 0
|
||||
- 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
|
||||
# on develop branch, only run a baseline scan
|
||||
- name: SonarCloud Scan (Baseline)
|
||||
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.projectKey=opencost_opencost
|
||||
-Dsonar.organization=opencost
|
||||
-Dsonar.branch.name=develop
|
||||
-Dsonar.branch.target=develop
|
||||
- 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 }}
|
||||
-Dsonar.projectKey=opencost_opencost
|
||||
-Dsonar.organization=opencost
|
||||
Reference in New Issue
Block a user