From a485528ebe2eb2f0ca7be747f38ac559edc4fb89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Fri, 28 Jun 2024 12:31:43 +0200 Subject: [PATCH] Refactor bash script parsing to improve coverage of env var injection --- ql/lib/codeql/actions/Helper.qll | 11 +++ ql/lib/codeql/actions/dataflow/FlowSteps.qll | 92 +++++++++++++++---- .../security/EnvPathInjectionQuery.qll | 27 +++--- .../actions/security/EnvVarInjectionQuery.qll | 29 +++--- .../actions/security/PoisonableSteps.qll | 16 ++-- ql/lib/ext/config/poisonable_steps.yml | 12 +-- .../CWE-077/.github/workflows/test6.yml | 28 ++++++ .../CWE-077/EnvVarInjectionCritical.expected | 12 +++ .../CWE-077/EnvVarInjectionMedium.expected | 9 ++ 9 files changed, 172 insertions(+), 64 deletions(-) create mode 100644 ql/test/query-tests/Security/CWE-077/.github/workflows/test6.yml diff --git a/ql/lib/codeql/actions/Helper.qll b/ql/lib/codeql/actions/Helper.qll index 401ba89eca7..72dc7bf1687 100644 --- a/ql/lib/codeql/actions/Helper.qll +++ b/ql/lib/codeql/actions/Helper.qll @@ -235,3 +235,14 @@ predicate inNonPrivilegedJob(AstNode node) { not j.isPrivilegedExternallyTriggerable() ) } + +bindingset[snippet] +predicate outputsPartialFileContent(string snippet) { + // e.g. + // echo FOO=`yq '.foo' foo.yml` >> $GITHUB_ENV + // echo "FOO=$(> $GITHUB_ENV + // yq '.foo' foo.yml >> $GITHUB_PATH + // cat foo.txt >> $GITHUB_PATH + snippet + .regexpMatch(["(\\$\\(|`)<.*", ".*(\\b|^|\\s+)" + ["cat\\s+", "jq\\s+", "yq\\s+", "tail\\s+", "head\\s+", "ls\\s+"] + ".*"]) +} diff --git a/ql/lib/codeql/actions/dataflow/FlowSteps.qll b/ql/lib/codeql/actions/dataflow/FlowSteps.qll index 4f4d80cc11b..caa09e9c7e2 100644 --- a/ql/lib/codeql/actions/dataflow/FlowSteps.qll +++ b/ql/lib/codeql/actions/dataflow/FlowSteps.qll @@ -23,6 +23,60 @@ class AdditionalTaintStep extends Unit { abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); } +/** + * Holds if an env var is passed to a Run step and this Run step, writes its value to a special workflow file. + * - file is the name of the special workflow file: GITHUB_ENV, GITHUB_OUTPUT, GITHUB_PATH + * - var_name is the name of the env var + * - run is the Run step + * - key is the name assigned in the special workflow file. + * e.g. FOO for `echo "FOO=$BODY" >> $GITHUB_ENV` + * e.g. FOO for `echo "FOO=$(echo $BODY)" >> $GITHUB_OUTPUT` + * e.g. path (special name) for `echo "$BODY" >> $GITHUB_PATH` + */ +bindingset[var_name] +predicate envToRunFlow(string file, string var_name, Run run, string key) { + exists(string content, string value | + ( + file = "GITHUB_ENV" and + writeToGitHubEnv(run, content) and + extractVariableAndValue(content, key, value) + or + file = "GITHUB_OUTPUT" and + writeToGitHubOutput(run, content) and + extractVariableAndValue(content, key, value) + or + file = "GITHUB_PATH" and + writeToGitHubPath(run, content) and + key = "path" and + value = content + ) and + ( + // e.g. echo "FOO=$BODY" >> $GITHUB_ENV + // e.g. echo "FOO=${BODY}" >> $GITHUB_ENV + value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") + or + // e.g. echo "FOO=$(echo $BODY)" >> $GITHUB_ENV + value.matches("$(echo %") and value.indexOf(var_name) > 0 + or + // e.g. + // FOO=$(echo $BODY) + // echo "FOO=$FOO" >> $GITHUB_ENV + exists(string line, string var2_name, string var2_value | + run.getScript().splitAt("\n") = line + | + var2_name = line.regexpCapture("([a-zA-Z0-9\\-_]+)=(.*)", 1) and + var2_value = line.regexpCapture("([a-zA-Z0-9\\-_]+)=(.*)", 2) and + var2_value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") and + ( + value.matches("%$" + ["", "{", "ENV{"] + var2_name + "%") + or + value.matches("$(echo %") and value.indexOf(var2_name) > 0 + ) + ) + ) + ) +} + /** * Holds if a Run step declares an environment variable, uses it in its script to set another env var. * e.g. @@ -32,20 +86,10 @@ class AdditionalTaintStep extends Unit { * echo "foo=$(echo $BODY)" >> $GITHUB_ENV */ predicate envToRunStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(Run run, string var_name, string content, string value | + exists(Run run, string var_name | run.getInScopeEnvVarExpr(var_name) = pred.asExpr() and - succ.asExpr() = run.getScriptScalar() - | - ( - writeToGitHubEnv(run, content) or - writeToGitHubOutput(run, content) - ) and - extractVariableAndValue(content, _, value) and - value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") - or - writeToGitHubPath(run, content) and - value = content and - value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") + succ.asExpr() = run.getScriptScalar() and + envToRunFlow(["GITHUB_ENV", "GITHUB_PATH"], var_name, run, _) ) } @@ -63,16 +107,26 @@ predicate envToRunStep(DataFlow::Node pred, DataFlow::Node succ) { * echo "::set-output name=step-output::$BODY" */ predicate envToOutputStoreStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::ContentSet c) { - exists(Run run, string var_name, string content, string key, string value | - writeToGitHubOutput(run, content) and - extractVariableAndValue(content, key, value) and - c = any(DataFlow::FieldContent ct | ct.getName() = key) and - pred.asExpr() = run.getInScopeEnvVarExpr(var_name) and + exists(Run run, string var_name, string key | + run.getInScopeEnvVarExpr(var_name) = pred.asExpr() and succ.asExpr() = run and - value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") + envToRunFlow("GITHUB_OUTPUT", var_name, run, key) and + c = any(DataFlow::FieldContent ct | ct.getName() = key) ) } +// predicate dISABLEDenvToOutputStoreStep( +// DataFlow::Node pred, DataFlow::Node succ, DataFlow::ContentSet c +// ) { +// exists(Run run, string var_name, string content, string key, string value | +// writeToGitHubOutput(run, content) and +// extractVariableAndValue(content, key, value) and +// c = any(DataFlow::FieldContent ct | ct.getName() = key) and +// pred.asExpr() = run.getInScopeEnvVarExpr(var_name) and +// succ.asExpr() = run and +// value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") +// ) +// } predicate envToEnvStoreStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::ContentSet c) { exists(Run run, string var_name, string content, string key, string value | writeToGitHubEnv(run, content) and diff --git a/ql/lib/codeql/actions/security/EnvPathInjectionQuery.qll b/ql/lib/codeql/actions/security/EnvPathInjectionQuery.qll index 453966f0101..cbdf9a917ce 100644 --- a/ql/lib/codeql/actions/security/EnvPathInjectionQuery.qll +++ b/ql/lib/codeql/actions/security/EnvPathInjectionQuery.qll @@ -1,22 +1,26 @@ 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 +private import codeql.actions.dataflow.FlowSteps import codeql.actions.DataFlow +import codeql.actions.dataflow.FlowSources abstract class EnvPathInjectionSink extends DataFlow::Node { } +/** + * Holds if a Run step declares a PATH environment variable with contents from a local file. + * e.g. + * run: | + * cat foo.txt >> $GITHUB_PATH + */ class EnvPathInjectionFromFileReadSink extends EnvPathInjectionSink { EnvPathInjectionFromFileReadSink() { exists(Run run, UntrustedArtifactDownloadStep step, string value | this.asExpr() = run.getScriptScalar() and step.getAFollowingStep() = run and writeToGitHubPath(run, value) and - // (eg: echo DATABASE_SHA=`yq '.creationMetadata.sha' codeql-database.yml` >> $GITHUB_ENV) - value - .regexpMatch(["\\$\\(", "`"] + - ["cat\\s+", "<", "jq\\s+", "yq\\s+", "tail\\s+", "head\\s+"] + ".*" + ["`", "\\)"]) + outputsPartialFileContent(value) ) } } @@ -31,15 +35,10 @@ class EnvPathInjectionFromFileReadSink extends EnvPathInjectionSink { */ class EnvPathInjectionFromEnvVarSink extends EnvPathInjectionSink { EnvPathInjectionFromEnvVarSink() { - exists(Run run, Expression expr, string var_name, string value | - run.getInScopeEnvVarExpr(var_name) = expr and - run.getScriptScalar() = this.asExpr() and - writeToGitHubPath(run, value) and - ( - value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") - or - value.matches("$(echo %") and value.indexOf(var_name) > 0 - ) + exists(Run run, string var_name | + envToRunFlow("GITHUB_PATH", var_name, run, _) and + exists(run.getInScopeEnvVarExpr(var_name)) and + run.getScriptScalar() = this.asExpr() ) } } diff --git a/ql/lib/codeql/actions/security/EnvVarInjectionQuery.qll b/ql/lib/codeql/actions/security/EnvVarInjectionQuery.qll index a78963086e1..5a3dbebc512 100644 --- a/ql/lib/codeql/actions/security/EnvVarInjectionQuery.qll +++ b/ql/lib/codeql/actions/security/EnvVarInjectionQuery.qll @@ -1,12 +1,20 @@ 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 +private import codeql.actions.dataflow.FlowSteps import codeql.actions.DataFlow +import codeql.actions.dataflow.FlowSources abstract class EnvVarInjectionSink extends DataFlow::Node { } +/** + * Holds if a Run step declares an environment variable with contents from a local file. + * e.g. + * run: | + * echo "sha=$(cat test-results/sha-number)" >> $GITHUB_ENV + * echo "sha=$(> $GITHUB_ENV + */ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink { EnvVarInjectionFromFileReadSink() { exists(Run run, UntrustedArtifactDownloadStep step, string content, string value | @@ -14,10 +22,7 @@ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink { step.getAFollowingStep() = run and writeToGitHubEnv(run, content) and extractVariableAndValue(content, _, value) and - // (eg: echo DATABASE_SHA=`yq '.creationMetadata.sha' codeql-database.yml` >> $GITHUB_ENV) - value - .regexpMatch(["\\$\\(", "`"] + - ["cat\\s+", "<", "jq\\s+", "yq\\s+", "tail\\s+", "head\\s+"] + ".*" + ["`", "\\)"]) + outputsPartialFileContent(value) ) } } @@ -32,16 +37,10 @@ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink { */ class EnvVarInjectionFromEnvVarSink extends EnvVarInjectionSink { EnvVarInjectionFromEnvVarSink() { - exists(Run run, Expression expr, string var_name, string content, string value | - run.getInScopeEnvVarExpr(var_name) = expr and - run.getScriptScalar() = this.asExpr() and - writeToGitHubEnv(run, content) and - extractVariableAndValue(content, _, value) and - ( - value.matches("%$" + ["", "{", "ENV{"] + var_name + "%") - or - value.matches("$(echo %") and value.indexOf(var_name) > 0 - ) + exists(Run run, string var_name | + envToRunFlow("GITHUB_ENV", var_name, run, _) and + exists(run.getInScopeEnvVarExpr(var_name)) and + run.getScriptScalar() = this.asExpr() ) } } diff --git a/ql/lib/codeql/actions/security/PoisonableSteps.qll b/ql/lib/codeql/actions/security/PoisonableSteps.qll index d9978b2a423..4165df17a4d 100644 --- a/ql/lib/codeql/actions/security/PoisonableSteps.qll +++ b/ql/lib/codeql/actions/security/PoisonableSteps.qll @@ -18,7 +18,7 @@ class PoisonableCommandStep extends PoisonableStep, Run { PoisonableCommandStep() { exists(string regexp | poisonableCommandsDataModel(regexp) and - exists(this.getScript().splitAt("\n").trim().regexpFind("([^a-z]|^)" + regexp, _, _)) + exists(this.getScript().splitAt("\n").trim().regexpFind("(^|\\b|\\s+)" + regexp, _, _)) ) } } @@ -29,7 +29,7 @@ class LocalScriptExecutionRunStep extends PoisonableStep, Run { LocalScriptExecutionRunStep() { exists(string line, string regexp, int group | line = this.getScript().splitAt("\n").trim() | poisonableLocalScriptsDataModel(regexp, group) and - cmd = line.regexpCapture(regexp, group) + cmd = line.regexpCapture("(^|\\b|\\s+)" + regexp, group) ) } @@ -40,16 +40,12 @@ class LocalActionUsesStep extends PoisonableStep, UsesStep { LocalActionUsesStep() { this.getCallee().matches("./%") } } -class EnvVarInjectionRunStep extends PoisonableStep, Run { - EnvVarInjectionRunStep() { - exists(string content, string value | - // Heuristic: - // Run step with env var definition based on file content. - // eg: `echo "sha=$(cat test-results/sha-number)" >> $GITHUB_ENV` - // eg: `echo "sha=$(> $GITHUB_ENV` +class EnvVarInjectionFromFileReadRunStep extends PoisonableStep, Run { + EnvVarInjectionFromFileReadRunStep() { + exists(string content, string value| writeToGitHubEnv(this, content) and extractVariableAndValue(content, _, value) and - value.matches("%" + ["ls ", "cat ", "jq ", "$(<"] + "%") + outputsPartialFileContent(value) ) } } diff --git a/ql/lib/ext/config/poisonable_steps.yml b/ql/lib/ext/config/poisonable_steps.yml index 11f17ae2623..dc835e7dab2 100644 --- a/ql/lib/ext/config/poisonable_steps.yml +++ b/ql/lib/ext/config/poisonable_steps.yml @@ -54,10 +54,10 @@ extensions: extensible: poisonableLocalScriptsDataModel data: # TODO: It could also be in the form of `dir/cmd` - - ["(^|;\\s*|\\s+)(\\.\\/)(.*)(\\s+|;|$)", 3] - - ["(^|;\\s*|\\s+)(source|sh|bash|zsh|fish)\\s+(.*)(\\s+|;|$)", 3] - - ["(^|;\\s*|\\s+)(node)\\s+(.*)(\\.js|\\.ts)(\\s+|;|$)", 3] - - ["(^|;\\s*|\\s+)(python)\\s+(.*)\\.py(\\s+|;|$)", 3] - - ["(^|;\\s*|\\s+)(ruby)\\s+(.*)\\.rb(\\s+|;|$)", 3] - - ["(^|;\\s*|\\s+)(go)\\s+(.*)\\.go(\\s+|;|$)", 3] + - ["(\\.\\/)(.*)(\\s+|;|$)", 3] + - ["(source|sh|bash|zsh|fish)\\s+(.*)(\\s+|;|$)", 3] + - ["(node)\\s+(.*)(\\.js|\\.ts)(\\s+|;|$)", 3] + - ["(python)\\s+(.*)\\.py(\\s+|;|$)", 3] + - ["(ruby)\\s+(.*)\\.rb(\\s+|;|$)", 3] + - ["(go)\\s+(.*)\\.go(\\s+|;|$)", 3] diff --git a/ql/test/query-tests/Security/CWE-077/.github/workflows/test6.yml b/ql/test/query-tests/Security/CWE-077/.github/workflows/test6.yml new file mode 100644 index 00000000000..36340258515 --- /dev/null +++ b/ql/test/query-tests/Security/CWE-077/.github/workflows/test6.yml @@ -0,0 +1,28 @@ +name: Test + +on: + pull_request_target: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - env: + TITLE: ${{ github.event.pull_request.title }} + run: | + FOO=${TITLE##*/} + echo PR_TITLE=${FOO} >> $GITHUB_ENV + - env: + TITLE: ${{ github.event.pull_request.title }} + run: | + FOO=$TITLE+ + echo PR_TITLE=$FOO >> $GITHUB_ENV + - env: + TITLE: ${{ github.event.pull_request.title }} + run: | + venv="$(echo $TITLE)')" + echo "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV + + + + diff --git a/ql/test/query-tests/Security/CWE-077/EnvVarInjectionCritical.expected b/ql/test/query-tests/Security/CWE-077/EnvVarInjectionCritical.expected index 0dbff955318..9c2fd6faf46 100644 --- a/ql/test/query-tests/Security/CWE-077/EnvVarInjectionCritical.expected +++ b/ql/test/query-tests/Security/CWE-077/EnvVarInjectionCritical.expected @@ -11,6 +11,9 @@ edges | .github/workflows/test4.yml:57:27:57:64 | github.event.pull_request.title | .github/workflows/test4.yml:55:14:55:70 | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | provenance | | | .github/workflows/test4.yml:60:19:60:56 | github.event.pull_request.title | .github/workflows/test4.yml:58:14:58:94 | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | provenance | | | .github/workflows/test5.yml:10:9:30:6 | Uses Step | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:16:19:16:56 | github.event.pull_request.title | .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:21:19:21:56 | github.event.pull_request.title | .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | provenance | | nodes | .github/workflows/test2.yml:12:9:41:6 | Uses Step | semmle.label | Uses Step | | .github/workflows/test2.yml:41:14:43:52 | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | semmle.label | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | @@ -36,6 +39,12 @@ nodes | .github/workflows/test4.yml:60:19:60:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test5.yml:10:9:30:6 | Uses Step | semmle.label | Uses Step | | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | semmle.label | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:11:19:11:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | semmle.label | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:16:19:16:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | semmle.label | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:21:19:21:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | semmle.label | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | subpaths #select | .github/workflows/test2.yml:41:14:43:52 | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | .github/workflows/test2.yml:12:9:41:6 | Uses Step | .github/workflows/test2.yml:41:14:43:52 | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test2.yml:41:14:43:52 | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | @@ -50,3 +59,6 @@ subpaths | .github/workflows/test4.yml:55:14:55:70 | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | .github/workflows/test4.yml:57:27:57:64 | github.event.pull_request.title | .github/workflows/test4.yml:55:14:55:70 | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test4.yml:55:14:55:70 | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | | .github/workflows/test4.yml:58:14:58:94 | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | .github/workflows/test4.yml:60:19:60:56 | github.event.pull_request.title | .github/workflows/test4.yml:58:14:58:94 | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test4.yml:58:14:58:94 | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | .github/workflows/test5.yml:10:9:30:6 | Uses Step | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | .github/workflows/test6.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | .github/workflows/test6.yml:16:19:16:56 | github.event.pull_request.title | .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | .github/workflows/test6.yml:21:19:21:56 | github.event.pull_request.title | .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | diff --git a/ql/test/query-tests/Security/CWE-077/EnvVarInjectionMedium.expected b/ql/test/query-tests/Security/CWE-077/EnvVarInjectionMedium.expected index 5641ea53afd..7ea9865c70a 100644 --- a/ql/test/query-tests/Security/CWE-077/EnvVarInjectionMedium.expected +++ b/ql/test/query-tests/Security/CWE-077/EnvVarInjectionMedium.expected @@ -11,6 +11,9 @@ edges | .github/workflows/test4.yml:57:27:57:64 | github.event.pull_request.title | .github/workflows/test4.yml:55:14:55:70 | echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV | provenance | | | .github/workflows/test4.yml:60:19:60:56 | github.event.pull_request.title | .github/workflows/test4.yml:58:14:58:94 | echo ISSUE_KEY=$(echo "${TITLE}" \| grep -oP 'ISPN-(?P[0-9]+)') >> $GITHUB_ENV | provenance | | | .github/workflows/test5.yml:10:9:30:6 | Uses Step | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:16:19:16:56 | github.event.pull_request.title | .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | provenance | | +| .github/workflows/test6.yml:21:19:21:56 | github.event.pull_request.title | .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | provenance | | nodes | .github/workflows/test2.yml:12:9:41:6 | Uses Step | semmle.label | Uses Step | | .github/workflows/test2.yml:41:14:43:52 | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | semmle.label | unzip pr.zip\necho "pr_number=$(cat NR)" >> $GITHUB_ENV\n | @@ -36,5 +39,11 @@ nodes | .github/workflows/test4.yml:60:19:60:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | | .github/workflows/test5.yml:10:9:30:6 | Uses Step | semmle.label | Uses Step | | .github/workflows/test5.yml:33:14:36:62 | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | semmle.label | echo "PR_NUM=$(cat coverage/pr_num.txt)" >> $GITHUB_ENV\necho "BASE=$(cat coverage/base.txt)" >> $GITHUB_ENV\necho "HEAD=$(cat coverage/head.txt)" >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:11:19:11:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:12:14:14:46 | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | semmle.label | FOO=${TITLE##*/}\necho PR_TITLE=${FOO} >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:16:19:16:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:17:14:19:44 | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | semmle.label | FOO=$TITLE+\necho PR_TITLE=$FOO >> $GITHUB_ENV\n | +| .github/workflows/test6.yml:21:19:21:56 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/test6.yml:22:14:24:52 | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | semmle.label | venv="$(echo $TITLE)')"\necho "VIRTUAL_ENV=${venv}" >> $GITHUB_ENV\n | subpaths #select