mirror of
https://github.com/github/codeql.git
synced 2025-12-26 13:46:31 +01:00
Refactor Script support
This commit is contained in:
@@ -22,6 +22,10 @@ class AstNode instanceof AstNodeImpl {
|
||||
CompositeAction getEnclosingCompositeAction() { result = super.getEnclosingCompositeAction() }
|
||||
|
||||
Expression getInScopeEnvVarExpr(string name) { result = super.getInScopeEnvVarExpr(name) }
|
||||
|
||||
ScalarValue getInScopeDefaultValue(string name, string prop) {
|
||||
result = super.getInScopeDefaultValue(name, prop)
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarValue extends AstNode instanceof ScalarValueImpl {
|
||||
@@ -121,6 +125,10 @@ class ReusableWorkflow extends Workflow instanceof ReusableWorkflowImpl {
|
||||
|
||||
class Input extends AstNode instanceof InputImpl { }
|
||||
|
||||
class Default extends AstNode instanceof DefaultsImpl {
|
||||
ScalarValue getValue(string name, string prop) { result = super.getValue(name, prop) }
|
||||
}
|
||||
|
||||
class Outputs extends AstNode instanceof OutputsImpl {
|
||||
Expression getAnOutputExpr() { result = super.getAnOutputExpr() }
|
||||
|
||||
@@ -286,14 +294,18 @@ class ExternalJob extends Job, Uses instanceof ExternalJobImpl { }
|
||||
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
|
||||
*/
|
||||
class Run extends Step instanceof RunImpl {
|
||||
string getScript() { result = super.getScript() }
|
||||
|
||||
ScalarValue getScriptScalar() { result = super.getScriptScalar() }
|
||||
ShellScript getScript() { result = super.getScript() }
|
||||
|
||||
Expression getAnScriptExpr() { result = super.getAnScriptExpr() }
|
||||
|
||||
string getWorkingDirectory() { result = super.getWorkingDirectory() }
|
||||
|
||||
string getShell() { result = super.getShell() }
|
||||
}
|
||||
|
||||
class ShellScript extends ScalarValueImpl instanceof ShellScriptImpl {
|
||||
string getRawScript() { result = super.getRawScript() }
|
||||
|
||||
string getStmt(int i) { result = super.getStmt(i) }
|
||||
|
||||
string getAStmt() { result = super.getAStmt() }
|
||||
@@ -302,19 +314,23 @@ class Run extends Step instanceof RunImpl {
|
||||
|
||||
string getACommand() { result = super.getACommand() }
|
||||
|
||||
predicate getAssignment(int i, string name, string value) { super.getAssignment(i, name, value) }
|
||||
string getFileReadCommand(int i) { result = super.getFileReadCommand(i) }
|
||||
|
||||
predicate getAnAssignment(string name, string value) { super.getAnAssignment(name, value) }
|
||||
string getAFileReadCommand() { result = super.getAFileReadCommand() }
|
||||
|
||||
predicate getAWriteToGitHubEnv(string name, string value) {
|
||||
super.getAWriteToGitHubEnv(name, value)
|
||||
predicate getAssignment(int i, string name, string data) { super.getAssignment(i, name, data) }
|
||||
|
||||
predicate getAnAssignment(string name, string data) { super.getAnAssignment(name, data) }
|
||||
|
||||
predicate getAWriteToGitHubEnv(string name, string data) {
|
||||
super.getAWriteToGitHubEnv(name, data)
|
||||
}
|
||||
|
||||
predicate getAWriteToGitHubOutput(string name, string value) {
|
||||
super.getAWriteToGitHubOutput(name, value)
|
||||
predicate getAWriteToGitHubOutput(string name, string data) {
|
||||
super.getAWriteToGitHubOutput(name, data)
|
||||
}
|
||||
|
||||
predicate getAWriteToGitHubPath(string value) { super.getAWriteToGitHubPath(value) }
|
||||
predicate getAWriteToGitHubPath(string data) { super.getAWriteToGitHubPath(data) }
|
||||
|
||||
predicate getAnEnvReachingGitHubOutputWrite(string var, string output_field) {
|
||||
super.getAnEnvReachingGitHubOutputWrite(var, output_field)
|
||||
@@ -331,6 +347,18 @@ class Run extends Step instanceof RunImpl {
|
||||
predicate getACmdReachingGitHubEnvWrite(string cmd, string output_field) {
|
||||
super.getACmdReachingGitHubEnvWrite(cmd, output_field)
|
||||
}
|
||||
|
||||
predicate getAnEnvReachingGitHubPathWrite(string var) {
|
||||
super.getAnEnvReachingGitHubPathWrite(var)
|
||||
}
|
||||
|
||||
predicate getACmdReachingGitHubPathWrite(string cmd) { super.getACmdReachingGitHubPathWrite(cmd) }
|
||||
|
||||
predicate fileToGitHubEnv(string path) { super.fileToGitHubEnv(path) }
|
||||
|
||||
predicate fileToGitHubOutput(string path) { super.fileToGitHubOutput(path) }
|
||||
|
||||
predicate fileToGitHubPath(string path) { super.fileToGitHubPath(path) }
|
||||
}
|
||||
|
||||
abstract class SimpleReferenceExpression extends AstNode instanceof SimpleReferenceExpressionImpl {
|
||||
|
||||
@@ -1,7 +1,303 @@
|
||||
private import codeql.actions.Ast
|
||||
private import codeql.Locations
|
||||
import codeql.actions.config.Config
|
||||
private import codeql.actions.security.ControlChecks
|
||||
|
||||
class BashShellScript extends ShellScript {
|
||||
BashShellScript() {
|
||||
exists(Run run |
|
||||
this = run.getScript() and
|
||||
run.getShell().matches("bash%")
|
||||
)
|
||||
}
|
||||
|
||||
private string lineProducer(int i) {
|
||||
result = this.getRawScript().regexpReplaceAll("\\\\\\s*\n", "").splitAt("\n", i)
|
||||
}
|
||||
|
||||
private predicate cmdSubstitutionReplacement(string cmdSubs, string id, int k) {
|
||||
exists(string line | line = this.lineProducer(k) |
|
||||
exists(int i, int j |
|
||||
cmdSubs =
|
||||
// $() cmd substitution
|
||||
line.regexpFind("\\$\\((?:[^()]+|\\((?:[^()]+|\\([^()]*\\))*\\))*\\)", i, j)
|
||||
.regexpReplaceAll("^\\$\\(", "")
|
||||
.regexpReplaceAll("\\)$", "") and
|
||||
id = "cmdsubs:" + k + ":" + i + ":" + j
|
||||
)
|
||||
or
|
||||
exists(int i, int j |
|
||||
// `...` cmd substitution
|
||||
cmdSubs =
|
||||
line.regexpFind("\\`[^\\`]+\\`", i, j)
|
||||
.regexpReplaceAll("^\\`", "")
|
||||
.regexpReplaceAll("\\`$", "") and
|
||||
id = "cmd:" + k + ":" + i + ":" + j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate rankedCmdSubstitutionReplacements(int i, string old, string new) {
|
||||
old = rank[i](string old2 | this.cmdSubstitutionReplacement(old2, _, _) | old2) and
|
||||
this.cmdSubstitutionReplacement(old, new, _)
|
||||
}
|
||||
|
||||
private predicate doReplaceCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.lineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doReplaceCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(target, replacement)
|
||||
)
|
||||
}
|
||||
|
||||
private string cmdSubstitutedLineProducer(int i) {
|
||||
// script lines where any command substitution has been replaced with a unique placeholder
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doReplaceCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
or
|
||||
this.cmdSubstitutionReplacement(result, _, i)
|
||||
}
|
||||
|
||||
private predicate quotedStringReplacement(string quotedStr, string id) {
|
||||
exists(string line, int k | line = this.cmdSubstitutedLineProducer(k) |
|
||||
exists(int i, int j |
|
||||
// double quoted string
|
||||
quotedStr = line.regexpFind("\"((?:[^\"\\\\]|\\\\.)*)\"", i, j) and
|
||||
id =
|
||||
"qstr:" + k + ":" + i + ":" + j + ":" + quotedStr.length() + ":" +
|
||||
quotedStr.regexpReplaceAll("[^a-zA-Z0-9]", "")
|
||||
)
|
||||
or
|
||||
exists(int i, int j |
|
||||
// single quoted string
|
||||
quotedStr = line.regexpFind("'((?:\\\\.|[^'\\\\])*)'", i, j) and
|
||||
id =
|
||||
"qstr:" + k + ":" + i + ":" + j + ":" + quotedStr.length() + ":" +
|
||||
quotedStr.regexpReplaceAll("[^a-zA-Z0-9]", "")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate rankedQuotedStringReplacements(int i, string old, string new) {
|
||||
old = rank[i](string old2 | this.quotedStringReplacement(old2, _) | old2) and
|
||||
this.quotedStringReplacement(old, new)
|
||||
}
|
||||
|
||||
private predicate doReplaceQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.cmdSubstitutedLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doReplaceQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(target, replacement)
|
||||
)
|
||||
}
|
||||
|
||||
private string quotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new | this.doReplaceQuotedStrings(i, round, _, new) | new order by round)
|
||||
}
|
||||
|
||||
private string stmtProducer(int i) {
|
||||
result = this.quotedStringLineProducer(i).splitAt(Bash::splitSeparator()).trim() and
|
||||
// when splitting the line with a separator that is not present, the result is the original line which may contain other separators
|
||||
// we only one the split parts that do not contain any of the separators
|
||||
not result.indexOf(Bash::splitSeparator()) > -1
|
||||
}
|
||||
|
||||
private predicate doStmtRestoreQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.stmtProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doStmtRestoreQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
private string restoredStmtQuotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doStmtRestoreQuotedStrings(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
private predicate doStmtRestoreCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.restoredStmtQuotedStringLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doStmtRestoreCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
override string getStmt(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doStmtRestoreCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
override string getAStmt() { result = this.getStmt(_) }
|
||||
|
||||
private string cmdProducer(int i) {
|
||||
result = this.quotedStringLineProducer(i).splitAt(Bash::separator()).trim() and
|
||||
// when splitting the line with a separator that is not present, the result is the original line which may contain other separators
|
||||
// we only one the split parts that do not contain any of the separators
|
||||
not result.indexOf(Bash::separator()) > -1
|
||||
}
|
||||
|
||||
private predicate doCmdRestoreQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.cmdProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doCmdRestoreQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
private string restoredCmdQuotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doCmdRestoreQuotedStrings(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
private predicate doCmdRestoreCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.restoredCmdQuotedStringLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doCmdRestoreCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
string getCmd(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doCmdRestoreCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
string getACmd() { result = this.getCmd(_) }
|
||||
|
||||
override string getCommand(int i) {
|
||||
result = this.getCmd(i) and
|
||||
// exclude variable declarations
|
||||
not result.regexpMatch("^[a-zA-Z0-9\\-_]+=") and
|
||||
// exclude the following keywords
|
||||
not result =
|
||||
[
|
||||
"", "for", "in", "do", "done", "if", "then", "else", "elif", "fi", "while", "until", "case",
|
||||
"esac", "{", "}"
|
||||
]
|
||||
}
|
||||
|
||||
override string getACommand() { result = this.getCommand(_) }
|
||||
|
||||
override string getFileReadCommand(int i) {
|
||||
result = this.getStmt(i) and
|
||||
result.matches(Bash::fileReadCommand() + "%")
|
||||
}
|
||||
|
||||
override string getAFileReadCommand() { result = this.getFileReadCommand(_) }
|
||||
|
||||
override predicate getAssignment(int i, string name, string data) {
|
||||
exists(string stmt |
|
||||
stmt = this.getStmt(i) and
|
||||
name = stmt.regexpCapture("^([a-zA-Z0-9\\-_]+)=.*", 1) and
|
||||
data = stmt.regexpCapture("^[a-zA-Z0-9\\-_]+=(.*)", 1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate getAnAssignment(string name, string data) { this.getAssignment(_, name, data) }
|
||||
|
||||
override predicate getAWriteToGitHubEnv(string name, string data) {
|
||||
exists(string raw |
|
||||
Bash::extractFileWrite(this.getRawScript(), "GITHUB_ENV", raw) and
|
||||
Bash::extractVariableAndValue(raw, name, data)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate getAWriteToGitHubOutput(string name, string data) {
|
||||
exists(string raw |
|
||||
Bash::extractFileWrite(this.getRawScript(), "GITHUB_OUTPUT", raw) and
|
||||
Bash::extractVariableAndValue(raw, name, data)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate getAWriteToGitHubPath(string data) {
|
||||
Bash::extractFileWrite(this.getRawScript(), "GITHUB_PATH", data)
|
||||
}
|
||||
|
||||
override predicate getAnEnvReachingGitHubOutputWrite(string var, string output_field) {
|
||||
Bash::envReachingGitHubFileWrite(this, var, "GITHUB_OUTPUT", output_field)
|
||||
}
|
||||
|
||||
override predicate getACmdReachingGitHubOutputWrite(string cmd, string output_field) {
|
||||
Bash::cmdReachingGitHubFileWrite(this, cmd, "GITHUB_OUTPUT", output_field)
|
||||
}
|
||||
|
||||
override predicate getAnEnvReachingGitHubEnvWrite(string var, string output_field) {
|
||||
Bash::envReachingGitHubFileWrite(this, var, "GITHUB_ENV", output_field)
|
||||
}
|
||||
|
||||
override predicate getACmdReachingGitHubEnvWrite(string cmd, string output_field) {
|
||||
Bash::cmdReachingGitHubFileWrite(this, cmd, "GITHUB_ENV", output_field)
|
||||
}
|
||||
|
||||
override predicate getAnEnvReachingGitHubPathWrite(string var) {
|
||||
Bash::envReachingGitHubFileWrite(this, var, "GITHUB_PATH", _)
|
||||
}
|
||||
|
||||
override predicate getACmdReachingGitHubPathWrite(string cmd) {
|
||||
Bash::cmdReachingGitHubFileWrite(this, cmd, "GITHUB_PATH", _)
|
||||
}
|
||||
|
||||
override predicate fileToGitHubEnv(string path) {
|
||||
Bash::fileToFileWrite(this, "GITHUB_ENV", path)
|
||||
}
|
||||
|
||||
override predicate fileToGitHubOutput(string path) {
|
||||
Bash::fileToFileWrite(this, "GITHUB_OUTPUT", path)
|
||||
}
|
||||
|
||||
override predicate fileToGitHubPath(string path) {
|
||||
Bash::fileToFileWrite(this, "GITHUB_PATH", path)
|
||||
}
|
||||
}
|
||||
|
||||
module Bash {
|
||||
string stmtSeparator() { result = ";" }
|
||||
@@ -23,7 +319,7 @@ module Bash {
|
||||
result = pipeSeparator()
|
||||
}
|
||||
|
||||
string partialFileContentCommand() { result = ["cat", "jq", "yq", "tail", "head"] }
|
||||
string fileReadCommand() { result = ["<", "cat", "jq", "yq", "tail", "head"] }
|
||||
|
||||
/** Checks if expr is a bash command substitution */
|
||||
bindingset[expr]
|
||||
@@ -133,8 +429,7 @@ module Bash {
|
||||
string script, string cmd, string file, string content, string filters
|
||||
) {
|
||||
exists(string regexp |
|
||||
regexp =
|
||||
"(?i)(echo|printf|write-output)\\s*(.*?)\\s*(>>|>|\\s*\\|\\s*tee\\s*(-a|--append)?)\\s*(\\S+)" and
|
||||
regexp = "(?i)(echo|printf)\\s*(.*?)\\s*(>>|>|\\s*\\|\\s*tee\\s*(-a|--append)?)\\s*(\\S+)" and
|
||||
cmd = script.regexpCapture(regexp, 1) and
|
||||
file = trimQuotes(script.regexpCapture(regexp, 5)) and
|
||||
filters = "" and
|
||||
@@ -145,13 +440,12 @@ module Bash {
|
||||
bindingset[script]
|
||||
predicate singleLineWorkflowCmd(string script, string cmd, string key, string value) {
|
||||
exists(string regexp |
|
||||
regexp =
|
||||
"(?i)(echo|printf|write-output)\\s*(['|\"])?::(set-[a-z]+)\\s*name\\s*=\\s*(.*?)::(.*)" and
|
||||
regexp = "(?i)(echo|printf)\\s*(['|\"])?::(set-[a-z]+)\\s*name\\s*=\\s*(.*?)::(.*)" and
|
||||
cmd = script.regexpCapture(regexp, 3) and
|
||||
key = script.regexpCapture(regexp, 4) and
|
||||
value = trimQuotes(script.regexpCapture(regexp, 5))
|
||||
or
|
||||
regexp = "(?i)(echo|printf|write-output)\\s*(['|\"])?::(add-[a-z]+)\\s*::(.*)" and
|
||||
regexp = "(?i)(echo|printf)\\s*(['|\"])?::(add-[a-z]+)\\s*::(.*)" and
|
||||
cmd = script.regexpCapture(regexp, 3) and
|
||||
key = "" and
|
||||
value = trimQuotes(script.regexpCapture(regexp, 4))
|
||||
@@ -262,57 +556,38 @@ module Bash {
|
||||
}
|
||||
|
||||
/** Writes the content of the file specified by `path` into a file pointed to by `file_var` */
|
||||
predicate fileToFileWrite(Run run, string file_var, string path) {
|
||||
predicate fileToFileWrite(BashShellScript script, string file_var, string path) {
|
||||
exists(string regexp, string stmt, string file_expr |
|
||||
regexp =
|
||||
"(?i)(cat)\\s*" + "((?:(?!<<|<<-)[^>\n])+)\\s*" +
|
||||
"(>>|>|\\s*\\|\\s*tee\\s*(-a|--append)?)\\s*" + "(\\S+)" and
|
||||
stmt = run.getAStmt() and
|
||||
stmt = script.getAStmt() and
|
||||
file_expr = trimQuotes(stmt.regexpCapture(regexp, 5)) and
|
||||
path = stmt.regexpCapture(regexp, 2) and
|
||||
containsParameterExpansion(file_expr, file_var, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate fileToGitHubEnv(Run run, string path) { fileToFileWrite(run, "GITHUB_ENV", path) }
|
||||
|
||||
predicate fileToGitHubOutput(Run run, string path) { fileToFileWrite(run, "GITHUB_OUTPUT", path) }
|
||||
|
||||
predicate fileToGitHubPath(Run run, string path) { fileToFileWrite(run, "GITHUB_PATH", path) }
|
||||
|
||||
bindingset[snippet]
|
||||
predicate outputsPartialFileContent(Run run, string snippet) {
|
||||
// e.g.
|
||||
// echo FOO=`yq '.foo' foo.yml` >> $GITHUB_ENV
|
||||
// echo "FOO=$(<foo.txt)" >> $GITHUB_ENV
|
||||
// yq '.foo' foo.yml >> $GITHUB_PATH
|
||||
// cat foo.txt >> $GITHUB_PATH
|
||||
exists(int i, string line, string cmd |
|
||||
run.getStmt(i) = line and
|
||||
line.indexOf(snippet.regexpReplaceAll("^\\$\\(", "").regexpReplaceAll("\\)$", "")) > -1 and
|
||||
run.getCommand(i) = cmd and
|
||||
cmd.indexOf(["<", Bash::partialFileContentCommand() + " "]) = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the Run scripts contains an access to an environment variable called `var`
|
||||
* which value may get appended to the GITHUB_XXX special file
|
||||
*/
|
||||
predicate envReachingGitHubFileWrite(Run run, string var, string file_var, string field) {
|
||||
predicate envReachingGitHubFileWrite(
|
||||
BashShellScript script, string var, string file_var, string field
|
||||
) {
|
||||
exists(string file_write_value |
|
||||
(
|
||||
file_var = "GITHUB_ENV" and
|
||||
run.getAWriteToGitHubEnv(field, file_write_value)
|
||||
script.getAWriteToGitHubEnv(field, file_write_value)
|
||||
or
|
||||
file_var = "GITHUB_OUTPUT" and
|
||||
run.getAWriteToGitHubOutput(field, file_write_value)
|
||||
script.getAWriteToGitHubOutput(field, file_write_value)
|
||||
or
|
||||
file_var = "GITHUB_PATH" and
|
||||
field = "PATH" and
|
||||
run.getAWriteToGitHubPath(file_write_value)
|
||||
script.getAWriteToGitHubPath(file_write_value)
|
||||
) and
|
||||
envReachingRunExpr(run, var, file_write_value)
|
||||
envReachingRunExpr(script, var, file_write_value)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -321,11 +596,11 @@ module Bash {
|
||||
* Where the expression is a string captured from the Run's script.
|
||||
*/
|
||||
bindingset[expr]
|
||||
predicate envReachingRunExpr(Run run, string var, string expr) {
|
||||
predicate envReachingRunExpr(BashShellScript script, string var, string expr) {
|
||||
exists(string var2, string value2 |
|
||||
// VAR2=${VAR:-default} (var2=value2)
|
||||
// echo "FIELD=${VAR2:-default}" >> $GITHUB_ENV (field, file_write_value)
|
||||
run.getAnAssignment(var2, value2) and
|
||||
script.getAnAssignment(var2, value2) and
|
||||
containsParameterExpansion(value2, var, _, _) and
|
||||
containsParameterExpansion(expr, var2, _, _)
|
||||
)
|
||||
@@ -339,33 +614,42 @@ module Bash {
|
||||
* Holds if the Run scripts contains a command substitution (`cmd`)
|
||||
* which output may get appended to the GITHUB_XXX special file
|
||||
*/
|
||||
predicate cmdReachingGitHubFileWrite(Run run, string cmd, string file_var, string field) {
|
||||
predicate cmdReachingGitHubFileWrite(
|
||||
BashShellScript script, string cmd, string file_var, string field
|
||||
) {
|
||||
exists(string file_write_value |
|
||||
(
|
||||
file_var = "GITHUB_ENV" and
|
||||
run.getAWriteToGitHubEnv(field, file_write_value)
|
||||
script.getAWriteToGitHubEnv(field, file_write_value)
|
||||
or
|
||||
file_var = "GITHUB_OUTPUT" and
|
||||
run.getAWriteToGitHubOutput(field, file_write_value)
|
||||
script.getAWriteToGitHubOutput(field, file_write_value)
|
||||
or
|
||||
file_var = "GITHUB_PATH" and
|
||||
field = "PATH" and
|
||||
run.getAWriteToGitHubPath(file_write_value)
|
||||
script.getAWriteToGitHubPath(file_write_value)
|
||||
) and
|
||||
(
|
||||
// cmd output is assigned to a second variable (var2) and var2 reaches the file write
|
||||
exists(string var2, string value2 |
|
||||
// VAR2=$(cmd)
|
||||
// echo "FIELD=${VAR2:-default}" >> $GITHUB_ENV (field, file_write_value)
|
||||
run.getAnAssignment(var2, value2) and
|
||||
containsCmdSubstitution(value2, cmd) and
|
||||
containsParameterExpansion(file_write_value, var2, _, _)
|
||||
)
|
||||
or
|
||||
// var reaches the file write directly
|
||||
// echo "FIELD=$(cmd)" >> $GITHUB_ENV (field, file_write_value)
|
||||
containsCmdSubstitution(file_write_value, cmd)
|
||||
)
|
||||
cmdReachingRunExpr(script, cmd, file_write_value)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a command output is used, directly or indirectly, in a Run's step expression.
|
||||
* Where the expression is a string captured from the Run's script.
|
||||
*/
|
||||
bindingset[expr]
|
||||
predicate cmdReachingRunExpr(BashShellScript script, string cmd, string expr) {
|
||||
// cmd output is assigned to a second variable (var2) and var2 reaches the file write
|
||||
exists(string var2, string value2 |
|
||||
// VAR2=$(cmd)
|
||||
// echo "FIELD=${VAR2:-default}" >> $GITHUB_ENV (field, file_write_value)
|
||||
script.getAnAssignment(var2, value2) and
|
||||
containsCmdSubstitution(value2, cmd) and
|
||||
containsParameterExpansion(expr, var2, _, _)
|
||||
)
|
||||
or
|
||||
// var reaches the file write directly
|
||||
// echo "FIELD=$(cmd)" >> $GITHUB_ENV (field, file_write_value)
|
||||
containsCmdSubstitution(expr, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ private import codeql.Locations
|
||||
private import codeql.actions.security.ControlChecks
|
||||
import codeql.actions.config.Config
|
||||
import codeql.actions.Bash
|
||||
import codeql.actions.PowerShell
|
||||
|
||||
bindingset[expr]
|
||||
string normalizeExpr(string expr) {
|
||||
|
||||
50
ql/lib/codeql/actions/PowerShell.qll
Normal file
50
ql/lib/codeql/actions/PowerShell.qll
Normal file
@@ -0,0 +1,50 @@
|
||||
private import codeql.actions.Ast
|
||||
|
||||
class PowerShellScript extends ShellScript {
|
||||
PowerShellScript() {
|
||||
exists(Run run |
|
||||
this = run.getScript() and
|
||||
run.getShell().matches("pwsh%")
|
||||
)
|
||||
}
|
||||
|
||||
override string getStmt(int i) { none() }
|
||||
|
||||
override string getAStmt() { none() }
|
||||
|
||||
override string getCommand(int i) { none() }
|
||||
|
||||
override string getACommand() { none() }
|
||||
|
||||
override string getFileReadCommand(int i) { none() }
|
||||
|
||||
override string getAFileReadCommand() { none() }
|
||||
|
||||
override predicate getAssignment(int i, string name, string data) { none() }
|
||||
|
||||
override predicate getAnAssignment(string name, string data) { none() }
|
||||
|
||||
override predicate getAWriteToGitHubEnv(string name, string data) { none() }
|
||||
|
||||
override predicate getAWriteToGitHubOutput(string name, string data) { none() }
|
||||
|
||||
override predicate getAWriteToGitHubPath(string data) { none() }
|
||||
|
||||
override predicate getAnEnvReachingGitHubOutputWrite(string var, string output_field) { none() }
|
||||
|
||||
override predicate getACmdReachingGitHubOutputWrite(string cmd, string output_field) { none() }
|
||||
|
||||
override predicate getAnEnvReachingGitHubEnvWrite(string var, string output_field) { none() }
|
||||
|
||||
override predicate getACmdReachingGitHubEnvWrite(string cmd, string output_field) { none() }
|
||||
|
||||
override predicate getAnEnvReachingGitHubPathWrite(string var) { none() }
|
||||
|
||||
override predicate getACmdReachingGitHubPathWrite(string cmd) { none() }
|
||||
|
||||
override predicate fileToGitHubEnv(string path) { none() }
|
||||
|
||||
override predicate fileToGitHubOutput(string path) { none() }
|
||||
|
||||
override predicate fileToGitHubPath(string path) { none() }
|
||||
}
|
||||
@@ -62,6 +62,7 @@ private newtype TAstNode =
|
||||
n.lookup("jobs") instanceof YamlMapping
|
||||
} or
|
||||
TRunsNode(YamlMapping n) { exists(CompositeActionImpl a | a.getNode().lookup("runs") = n) } or
|
||||
TDefaultsNode(YamlMapping n) { exists(YamlMapping m | m.lookup("defaults") = n) } or
|
||||
TInputsNode(YamlMapping n) { exists(YamlMapping m | m.lookup("inputs") = n) } or
|
||||
TInputNode(YamlValue n) { exists(YamlMapping m | m.lookup("inputs").(YamlMapping).maps(n, _)) } or
|
||||
TOutputsNode(YamlMapping n) { exists(YamlMapping m | m.lookup("outputs") = n) } or
|
||||
@@ -141,6 +142,19 @@ abstract class AstNodeImpl extends TAstNode {
|
||||
env.getParentNode().getAChildNode*() = this
|
||||
)
|
||||
}
|
||||
|
||||
ScalarValueImpl getInScopeDefaultValue(string name, string prop) {
|
||||
exists(DefaultsImpl dft |
|
||||
this.getEnclosingJob().getNode().(YamlMapping).maps(_, dft.getNode()) and
|
||||
result = dft.getValue(name, prop)
|
||||
)
|
||||
or
|
||||
not exists(DefaultsImpl dft | this.getEnclosingJob() = dft.getParentNode()) and
|
||||
exists(DefaultsImpl dft |
|
||||
this.getEnclosingWorkflow().getNode().(YamlMapping).maps(_, dft.getNode()) and
|
||||
result = dft.getValue(name, prop)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarValueImpl extends AstNodeImpl, TScalarValueNode {
|
||||
@@ -165,6 +179,61 @@ class ScalarValueImpl extends AstNodeImpl, TScalarValueNode {
|
||||
string getValue() { result = value.getValue() }
|
||||
}
|
||||
|
||||
class ShellScriptImpl extends ScalarValueImpl {
|
||||
ShellScriptImpl() { exists(YamlMapping run | run.lookup("run").(YamlScalar) = this.getNode()) }
|
||||
|
||||
string getRawScript() { result = this.getValue().regexpReplaceAll("\\\\\\s*\n", "") }
|
||||
|
||||
RunImpl getEnclosingRun() { result.getNode().lookup("run") = this.getNode() }
|
||||
|
||||
abstract string getStmt(int i);
|
||||
|
||||
abstract string getAStmt();
|
||||
|
||||
abstract string getCommand(int i);
|
||||
|
||||
string getACommand() {
|
||||
if this.getEnclosingRun().getShell().matches("bash%")
|
||||
then result = this.(BashShellScript).getACommand()
|
||||
else
|
||||
if this.getEnclosingRun().getShell().matches("pwsh%")
|
||||
then result = this.(PowerShellScript).getACommand()
|
||||
else result = "NOT IMPLEMENTED"
|
||||
}
|
||||
|
||||
abstract string getFileReadCommand(int i);
|
||||
|
||||
abstract string getAFileReadCommand();
|
||||
|
||||
abstract predicate getAssignment(int i, string name, string data);
|
||||
|
||||
abstract predicate getAnAssignment(string name, string data);
|
||||
|
||||
abstract predicate getAWriteToGitHubEnv(string name, string data);
|
||||
|
||||
abstract predicate getAWriteToGitHubOutput(string name, string data);
|
||||
|
||||
abstract predicate getAWriteToGitHubPath(string data);
|
||||
|
||||
abstract predicate getAnEnvReachingGitHubOutputWrite(string var, string output_field);
|
||||
|
||||
abstract predicate getACmdReachingGitHubOutputWrite(string cmd, string output_field);
|
||||
|
||||
abstract predicate getAnEnvReachingGitHubEnvWrite(string var, string output_field);
|
||||
|
||||
abstract predicate getACmdReachingGitHubEnvWrite(string cmd, string output_field);
|
||||
|
||||
abstract predicate getAnEnvReachingGitHubPathWrite(string var);
|
||||
|
||||
abstract predicate getACmdReachingGitHubPathWrite(string cmd);
|
||||
|
||||
abstract predicate fileToGitHubEnv(string path);
|
||||
|
||||
abstract predicate fileToGitHubOutput(string path);
|
||||
|
||||
abstract predicate fileToGitHubPath(string path);
|
||||
}
|
||||
|
||||
class ExpressionImpl extends AstNodeImpl, TExpressionNode {
|
||||
YamlNode key;
|
||||
YamlString value;
|
||||
@@ -493,6 +562,28 @@ class InputsImpl extends AstNodeImpl, TInputsNode {
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultsImpl extends AstNodeImpl, TDefaultsNode {
|
||||
YamlMapping n;
|
||||
|
||||
DefaultsImpl() { this = TDefaultsNode(n) }
|
||||
|
||||
override string toString() { result = n.toString() }
|
||||
|
||||
override AstNodeImpl getAChildNode() { result.getNode() = n.getAChildNode*() }
|
||||
|
||||
override AstNodeImpl getParentNode() { result.getAChildNode() = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DefaultsImpl" }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
|
||||
override YamlMapping getNode() { result = n }
|
||||
|
||||
ScalarValueImpl getValue(string name, string prop) {
|
||||
n.lookup(name).(YamlMapping).lookup(prop) = result.getNode()
|
||||
}
|
||||
}
|
||||
|
||||
class InputImpl extends AstNodeImpl, TInputNode {
|
||||
YamlValue n;
|
||||
|
||||
@@ -1314,20 +1405,18 @@ class ExternalJobImpl extends JobImpl, UsesImpl {
|
||||
|
||||
class RunImpl extends StepImpl {
|
||||
YamlScalar script;
|
||||
ScalarValueImpl scriptScalar;
|
||||
|
||||
RunImpl() { this.getNode().lookup("run") = script }
|
||||
|
||||
string getScript() { result = script.getValue().regexpReplaceAll("\\\\\\s*\n", "") }
|
||||
|
||||
ScalarValueImpl getScriptScalar() { result = TScalarValueNode(script) }
|
||||
|
||||
ExpressionImpl getAnScriptExpr() { result.getParentNode().getNode() = script }
|
||||
RunImpl() {
|
||||
this.getNode().lookup("run") = script and
|
||||
scriptScalar = TScalarValueNode(script)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
if exists(this.getId()) then result = "Run Step: " + this.getId() else result = "Run Step"
|
||||
}
|
||||
|
||||
/** Gets the working directory for this `runs` mapping. */
|
||||
/** Gets the working directory for this `run` mapping. */
|
||||
string getWorkingDirectory() {
|
||||
if exists(n.lookup("working-directory").(YamlString).getValue())
|
||||
then
|
||||
@@ -1339,268 +1428,19 @@ class RunImpl extends StepImpl {
|
||||
else result = "GITHUB_WORKSPACE/"
|
||||
}
|
||||
|
||||
private string lineProducer(int i) {
|
||||
result = script.getValue().regexpReplaceAll("\\\\\\s*\n", "").splitAt("\n", i)
|
||||
/** Gets the shell for this `run` mapping. */
|
||||
string getShell() {
|
||||
if exists(n.lookup("shell").(YamlString).getValue())
|
||||
then result = n.lookup("shell").(YamlString).getValue()
|
||||
else
|
||||
if exists(this.getInScopeDefaultValue("run", "shell"))
|
||||
then result = this.getInScopeDefaultValue("run", "shell").getValue()
|
||||
else result = "bash"
|
||||
}
|
||||
|
||||
private predicate cmdSubstitutionReplacement(string cmdSubs, string id, int k) {
|
||||
exists(string line | line = this.lineProducer(k) |
|
||||
exists(int i, int j |
|
||||
cmdSubs =
|
||||
// $() cmd substitution
|
||||
line.regexpFind("\\$\\((?:[^()]+|\\((?:[^()]+|\\([^()]*\\))*\\))*\\)", i, j)
|
||||
.regexpReplaceAll("^\\$\\(", "")
|
||||
.regexpReplaceAll("\\)$", "") and
|
||||
id = "cmdsubs:" + k + ":" + i + ":" + j
|
||||
)
|
||||
or
|
||||
exists(int i, int j |
|
||||
// `...` cmd substitution
|
||||
cmdSubs =
|
||||
line.regexpFind("\\`[^\\`]+\\`", i, j)
|
||||
.regexpReplaceAll("^\\`", "")
|
||||
.regexpReplaceAll("\\`$", "") and
|
||||
id = "cmd:" + k + ":" + i + ":" + j
|
||||
)
|
||||
)
|
||||
}
|
||||
ShellScriptImpl getScript() { result = scriptScalar }
|
||||
|
||||
private predicate rankedCmdSubstitutionReplacements(int i, string old, string new) {
|
||||
old = rank[i](string old2 | this.cmdSubstitutionReplacement(old2, _, _) | old2) and
|
||||
this.cmdSubstitutionReplacement(old, new, _)
|
||||
}
|
||||
|
||||
private predicate doReplaceCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.lineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doReplaceCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(target, replacement)
|
||||
)
|
||||
}
|
||||
|
||||
private string cmdSubstitutedLineProducer(int i) {
|
||||
// script lines where any command substitution has been replaced with a unique placeholder
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doReplaceCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
or
|
||||
this.cmdSubstitutionReplacement(result, _, i)
|
||||
}
|
||||
|
||||
private predicate quotedStringReplacement(string quotedStr, string id) {
|
||||
exists(string line, int k | line = this.cmdSubstitutedLineProducer(k) |
|
||||
exists(int i, int j |
|
||||
// double quoted string
|
||||
quotedStr = line.regexpFind("\"((?:[^\"\\\\]|\\\\.)*)\"", i, j) and
|
||||
id =
|
||||
"qstr:" + k + ":" + i + ":" + j + ":" + quotedStr.length() + ":" +
|
||||
quotedStr.regexpReplaceAll("[^a-zA-Z0-9]", "")
|
||||
)
|
||||
or
|
||||
exists(int i, int j |
|
||||
// single quoted string
|
||||
quotedStr = line.regexpFind("'((?:\\\\.|[^'\\\\])*)'", i, j) and
|
||||
id =
|
||||
"qstr:" + k + ":" + i + ":" + j + ":" + quotedStr.length() + ":" +
|
||||
quotedStr.regexpReplaceAll("[^a-zA-Z0-9]", "")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate rankedQuotedStringReplacements(int i, string old, string new) {
|
||||
old = rank[i](string old2 | this.quotedStringReplacement(old2, _) | old2) and
|
||||
this.quotedStringReplacement(old, new)
|
||||
}
|
||||
|
||||
private predicate doReplaceQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.cmdSubstitutedLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doReplaceQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(target, replacement)
|
||||
)
|
||||
}
|
||||
|
||||
private string quotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new | this.doReplaceQuotedStrings(i, round, _, new) | new order by round)
|
||||
}
|
||||
|
||||
private string stmtProducer(int i) {
|
||||
result = this.quotedStringLineProducer(i).splitAt(Bash::splitSeparator()).trim() and
|
||||
// when splitting the line with a separator that is not present, the result is the original line which may contain other separators
|
||||
// we only one the split parts that do not contain any of the separators
|
||||
not result.indexOf(Bash::splitSeparator()) > -1
|
||||
}
|
||||
|
||||
private predicate doStmtRestoreQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.stmtProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doStmtRestoreQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
private string restoredStmtQuotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doStmtRestoreQuotedStrings(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
private predicate doStmtRestoreCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.restoredStmtQuotedStringLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doStmtRestoreCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
string getStmt(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doStmtRestoreCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
string getAStmt() { result = this.getStmt(_) }
|
||||
|
||||
private string cmdProducer(int i) {
|
||||
result = this.quotedStringLineProducer(i).splitAt(Bash::separator()).trim() and
|
||||
// when splitting the line with a separator that is not present, the result is the original line which may contain other separators
|
||||
// we only one the split parts that do not contain any of the separators
|
||||
not result.indexOf(Bash::separator()) > -1
|
||||
}
|
||||
|
||||
private predicate doCmdRestoreQuotedStrings(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.cmdProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doCmdRestoreQuotedStrings(line, round - 1, old, middle) and
|
||||
this.rankedQuotedStringReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
private string restoredCmdQuotedStringLineProducer(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doCmdRestoreQuotedStrings(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
private predicate doCmdRestoreCmdSubstitutions(int line, int round, string old, string new) {
|
||||
round = 0 and
|
||||
old = this.restoredCmdQuotedStringLineProducer(line) and
|
||||
new = old
|
||||
or
|
||||
round > 0 and
|
||||
exists(string middle, string target, string replacement |
|
||||
this.doCmdRestoreCmdSubstitutions(line, round - 1, old, middle) and
|
||||
this.rankedCmdSubstitutionReplacements(round, target, replacement) and
|
||||
new = middle.replaceAll(replacement, target)
|
||||
)
|
||||
}
|
||||
|
||||
string getCmd(int i) {
|
||||
result =
|
||||
max(int round, string new |
|
||||
this.doCmdRestoreCmdSubstitutions(i, round, _, new)
|
||||
|
|
||||
new order by round
|
||||
)
|
||||
}
|
||||
|
||||
string getACmd() { result = this.getCmd(_) }
|
||||
|
||||
string getCommand(int i) {
|
||||
result = this.getCmd(i) and
|
||||
// exclude variable declarations
|
||||
not result.regexpMatch("^[a-zA-Z0-9\\-_]+=") and
|
||||
// exclude the following keywords
|
||||
not result =
|
||||
[
|
||||
"", "for", "in", "do", "done", "if", "then", "else", "elif", "fi", "while", "until", "case",
|
||||
"esac", "{", "}"
|
||||
]
|
||||
}
|
||||
|
||||
string getACommand() { result = this.getCommand(_) }
|
||||
|
||||
predicate getAssignment(int i, string name, string value) {
|
||||
exists(string stmt |
|
||||
stmt = this.getStmt(i) and
|
||||
name = stmt.regexpCapture("^([a-zA-Z0-9\\-_]+)=.*", 1) and
|
||||
value = stmt.regexpCapture("^[a-zA-Z0-9\\-_]+=(.*)", 1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate getAnAssignment(string name, string value) { this.getAssignment(_, name, value) }
|
||||
|
||||
predicate getAWriteToGitHubEnv(string name, string value) {
|
||||
exists(string raw |
|
||||
Bash::extractFileWrite(this.getScript(), "GITHUB_ENV", raw) and
|
||||
Bash::extractVariableAndValue(raw, name, value)
|
||||
)
|
||||
}
|
||||
|
||||
predicate getAWriteToGitHubOutput(string name, string value) {
|
||||
exists(string raw |
|
||||
Bash::extractFileWrite(this.getScript(), "GITHUB_OUTPUT", raw) and
|
||||
Bash::extractVariableAndValue(raw, name, value)
|
||||
)
|
||||
}
|
||||
|
||||
predicate getAWriteToGitHubPath(string value) {
|
||||
Bash::extractFileWrite(this.getScript(), "GITHUB_PATH", value)
|
||||
}
|
||||
|
||||
predicate getAnEnvReachingGitHubOutputWrite(string var, string output_field) {
|
||||
Bash::envReachingGitHubFileWrite(this, var, "GITHUB_OUTPUT", output_field)
|
||||
}
|
||||
|
||||
predicate getACmdReachingGitHubOutputWrite(string cmd, string output_field) {
|
||||
Bash::cmdReachingGitHubFileWrite(this, cmd, "GITHUB_OUTPUT", output_field)
|
||||
}
|
||||
|
||||
predicate getAnEnvReachingGitHubEnvWrite(string var, string output_field) {
|
||||
Bash::envReachingGitHubFileWrite(this, var, "GITHUB_ENV", output_field)
|
||||
}
|
||||
|
||||
predicate getACmdReachingGitHubEnvWrite(string cmd, string output_field) {
|
||||
Bash::cmdReachingGitHubFileWrite(this, cmd, "GITHUB_ENV", output_field)
|
||||
}
|
||||
ExpressionImpl getAnScriptExpr() { result.getParentNode().getNode() = script }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -282,7 +282,7 @@ private class RunTree extends StandardPreOrderTree instanceof Run {
|
||||
(
|
||||
child = super.getInScopeEnvVarExpr(_) or
|
||||
child = super.getAnScriptExpr() or
|
||||
child = super.getScriptScalar()
|
||||
child = super.getScript()
|
||||
) and
|
||||
l = child.getLocation()
|
||||
|
|
||||
|
||||
@@ -86,7 +86,8 @@ class GitCommandSource extends RemoteFlowSource, CommandSource {
|
||||
exists(Uses uses |
|
||||
checkout = uses and
|
||||
uses.getCallee() = "actions/checkout" and
|
||||
exists(uses.getArgument("ref"))
|
||||
exists(uses.getArgument("ref")) and
|
||||
not uses.getArgument("ref").matches("%base%")
|
||||
)
|
||||
or
|
||||
checkout instanceof GitMutableRefCheckout
|
||||
@@ -97,9 +98,9 @@ class GitCommandSource extends RemoteFlowSource, CommandSource {
|
||||
or
|
||||
checkout instanceof GhSHACheckout
|
||||
) and
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
this.asExpr() = run.getScript() and
|
||||
checkout.getAFollowingStep() = run and
|
||||
run.getACommand() = cmd and
|
||||
run.getScript().getACommand() = cmd and
|
||||
cmd.indexOf("git") = 0 and
|
||||
untrustedGitCommandsDataModel(cmd_regex, flag) and
|
||||
cmd.regexpMatch(cmd_regex)
|
||||
@@ -127,8 +128,8 @@ class GitHubEventPathSource extends RemoteFlowSource, CommandSource {
|
||||
// PR_TITLE=$(jq --raw-output .pull_request.title ${GITHUB_EVENT_PATH})
|
||||
// BODY=$(jq -r '.issue.body' "$GITHUB_EVENT_PATH" | sed -n '3p')
|
||||
GitHubEventPathSource() {
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
run.getACommand() = cmd and
|
||||
this.asExpr() = run.getScript() and
|
||||
run.getScript().getACommand() = cmd and
|
||||
cmd.matches("jq%") and
|
||||
cmd.matches("%GITHUB_EVENT_PATH%") and
|
||||
exists(string regexp |
|
||||
@@ -207,10 +208,11 @@ private class CheckoutSource extends RemoteFlowSource, FileSource {
|
||||
// but PRHeadCheckoutStep uses Taint Tracking anc causes a non-Monolitic Recursion error
|
||||
// so we list all the subclasses of PRHeadCheckoutStep here and use actions/checkout as a workaround
|
||||
// instead of using ActionsMutableRefCheckout and ActionsSHACheckout
|
||||
exists(Uses u |
|
||||
this.asExpr() = u and
|
||||
u.getCallee() = "actions/checkout" and
|
||||
exists(u.getArgument("ref"))
|
||||
exists(Uses uses |
|
||||
this.asExpr() = uses and
|
||||
uses.getCallee() = "actions/checkout" and
|
||||
exists(uses.getArgument("ref")) and
|
||||
not uses.getArgument("ref").matches("%base%")
|
||||
)
|
||||
or
|
||||
this.asExpr() instanceof GitMutableRefCheckout
|
||||
|
||||
@@ -23,7 +23,7 @@ predicate envToOutputStoreStep(DataFlow::Node pred, DataFlow::Node succ, DataFlo
|
||||
exists(Run run, string var, string field |
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
succ.asExpr() = run and
|
||||
Bash::envReachingGitHubFileWrite(run, var, "GITHUB_OUTPUT", field) and
|
||||
run.getScript().getAnEnvReachingGitHubOutputWrite(var, field) and
|
||||
c = any(DataFlow::FieldContent ct | ct.getName() = field)
|
||||
)
|
||||
}
|
||||
@@ -35,8 +35,8 @@ predicate envToEnvStoreStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
// we store the taint on the enclosing job since the may not exist an implicit env attribute
|
||||
succ.asExpr() = run.getEnclosingJob() and
|
||||
Bash::envReachingGitHubFileWrite(run, var, "GITHUB_ENV", field) and
|
||||
c = any(DataFlow::FieldContent ct | ct.getName() = field) //and
|
||||
run.getScript().getAnEnvReachingGitHubEnvWrite(var, field) and
|
||||
c = any(DataFlow::FieldContent ct | ct.getName() = field)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,12 +55,12 @@ predicate commandToOutputStoreStep(DataFlow::Node pred, DataFlow::Node succ, Dat
|
||||
or
|
||||
exists(FileSource source |
|
||||
source.asExpr().(Step).getAFollowingStep() = run and
|
||||
Bash::outputsPartialFileContent(run, cmd)
|
||||
run.getScript().getAFileReadCommand() = cmd
|
||||
)
|
||||
) and
|
||||
Bash::cmdReachingGitHubFileWrite(run, cmd, "GITHUB_OUTPUT", key) and
|
||||
run.getScript().getACmdReachingGitHubOutputWrite(cmd, key) and
|
||||
c = any(DataFlow::FieldContent ct | ct.getName() = key) and
|
||||
pred.asExpr() = run.getScriptScalar() and
|
||||
pred.asExpr() = run.getScript() and
|
||||
succ.asExpr() = run
|
||||
)
|
||||
}
|
||||
@@ -80,12 +80,12 @@ predicate commandToEnvStoreStep(DataFlow::Node pred, DataFlow::Node succ, DataFl
|
||||
or
|
||||
exists(FileSource source |
|
||||
source.asExpr().(Step).getAFollowingStep() = run and
|
||||
Bash::outputsPartialFileContent(run, cmd)
|
||||
run.getScript().getAFileReadCommand() = cmd
|
||||
)
|
||||
) and
|
||||
Bash::cmdReachingGitHubFileWrite(run, cmd, "GITHUB_ENV", key) and
|
||||
run.getScript().getACmdReachingGitHubEnvWrite(cmd, key) and
|
||||
c = any(DataFlow::FieldContent ct | ct.getName() = key) and
|
||||
pred.asExpr() = run.getScriptScalar() and
|
||||
pred.asExpr() = run.getScript() and
|
||||
// we store the taint on the enclosing job since there may not be an implicit env attribute
|
||||
succ.asExpr() = run.getEnclosingJob()
|
||||
)
|
||||
|
||||
@@ -22,14 +22,14 @@ class AdditionalTaintStep extends Unit {
|
||||
}
|
||||
|
||||
/**
|
||||
* A download artifact step followed by a step that may use downloaded artifacts.
|
||||
* A file source step followed by a Run step may read the file.
|
||||
*/
|
||||
predicate fileDownloadToRunStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(FileSource source, Run run |
|
||||
pred = source and
|
||||
source.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ abstract class ArgumentInjectionSink extends DataFlow::Node {
|
||||
bindingset[var]
|
||||
predicate envToArgInjSink(string var, Run run, string command) {
|
||||
exists(string argument, string cmd, string regexp, int command_group, int argument_group |
|
||||
run.getACommand() = cmd and
|
||||
run.getScript().getACommand() = cmd and
|
||||
argumentInjectionSinksDataModel(regexp, command_group, argument_group) and
|
||||
command = cmd.regexpCapture(regexp, command_group) and
|
||||
argument = cmd.regexpCapture(regexp, argument_group) and
|
||||
Bash::envReachingRunExpr(run, var, argument) and
|
||||
Bash::envReachingRunExpr(run.getScript(), var, argument) and
|
||||
exists(run.getInScopeEnvVarExpr(var))
|
||||
)
|
||||
}
|
||||
@@ -40,15 +40,15 @@ class ArgumentInjectionFromEnvVarSink extends ArgumentInjectionSink {
|
||||
ArgumentInjectionFromEnvVarSink() {
|
||||
exists(Run run, string var |
|
||||
envToArgInjSink(var, run, command) and
|
||||
run.getScriptScalar() = this.asExpr() and
|
||||
run.getScript() = this.asExpr() and
|
||||
exists(run.getInScopeEnvVarExpr(var))
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Run run, string cmd, string argument, string regexp, int argument_group, int command_group
|
||||
|
|
||||
run.getACommand() = cmd and
|
||||
run.getScriptScalar() = this.asExpr() and
|
||||
run.getScript().getACommand() = cmd and
|
||||
run.getScript() = this.asExpr() and
|
||||
argumentInjectionSinksDataModel(regexp, command_group, argument_group) and
|
||||
argument = cmd.regexpCapture(regexp, argument_group) and
|
||||
command = cmd.regexpCapture(regexp, command_group) and
|
||||
@@ -75,8 +75,8 @@ class ArgumentInjectionFromCommandSink extends ArgumentInjectionSink {
|
||||
int command_group
|
||||
|
|
||||
run = source.getEnclosingRun() and
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
cmd = run.getACommand() and
|
||||
this.asExpr() = run.getScript() and
|
||||
cmd = run.getScript().getACommand() and
|
||||
argumentInjectionSinksDataModel(regexp, command_group, argument_group) and
|
||||
argument = cmd.regexpCapture(regexp, argument_group) and
|
||||
command = cmd.regexpCapture(regexp, command_group)
|
||||
@@ -106,8 +106,8 @@ private module ArgumentInjectionConfig implements DataFlow::ConfigSig {
|
||||
exists(
|
||||
Run run, string argument, string cmd, string regexp, int command_group, int argument_group
|
||||
|
|
||||
run.getScriptScalar() = source.asExpr() and
|
||||
run.getACommand() = cmd and
|
||||
run.getScript() = source.asExpr() and
|
||||
run.getScript().getACommand() = cmd and
|
||||
argumentInjectionSinksDataModel(regexp, command_group, argument_group) and
|
||||
argument = cmd.regexpCapture(regexp, argument_group) and
|
||||
argument.regexpMatch(".*\\$(\\{)?(GITHUB_HEAD_REF).*")
|
||||
@@ -119,7 +119,7 @@ private module ArgumentInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Run run, string var |
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
succ.asExpr() = run.getScript() and
|
||||
envToArgInjSink(var, run, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -155,15 +155,21 @@ class ActionsGitHubScriptDownloadStep extends UntrustedArtifactDownloadStep, Use
|
||||
}
|
||||
|
||||
override string getPath() {
|
||||
if this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
if
|
||||
this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
then
|
||||
result =
|
||||
normalizePath(trimQuotes(this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)))
|
||||
else
|
||||
if this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp())
|
||||
if this.getAFollowingStep().(Run).getScript().getACommand().regexpMatch(unzipRegexp())
|
||||
then result = "GITHUB_WORKSPACE/"
|
||||
else none()
|
||||
}
|
||||
@@ -172,31 +178,37 @@ class ActionsGitHubScriptDownloadStep extends UntrustedArtifactDownloadStep, Use
|
||||
class GHRunArtifactDownloadStep extends UntrustedArtifactDownloadStep, Run {
|
||||
GHRunArtifactDownloadStep() {
|
||||
// eg: - run: gh run download ${{ github.event.workflow_run.id }} --repo "${GITHUB_REPOSITORY}" --name "artifact_name"
|
||||
this.getACommand().regexpMatch(".*gh\\s+run\\s+download.*") and
|
||||
this.getACommand().matches("%github.event.workflow_run.id%") and
|
||||
this.getScript().getACommand().regexpMatch(".*gh\\s+run\\s+download.*") and
|
||||
this.getScript().getACommand().matches("%github.event.workflow_run.id%") and
|
||||
(
|
||||
this.getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp())
|
||||
this.getScript().getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getAFollowingStep().(Run).getScript().getACommand().regexpMatch(unzipRegexp())
|
||||
)
|
||||
}
|
||||
|
||||
override string getPath() {
|
||||
if
|
||||
this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp()) or
|
||||
this.getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpMatch(unzipRegexp() + unzipDirArgRegexp()) or
|
||||
this.getScript().getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
then
|
||||
result =
|
||||
normalizePath(trimQuotes(this.getACommand()
|
||||
normalizePath(trimQuotes(this.getScript()
|
||||
.getACommand()
|
||||
.regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) or
|
||||
result =
|
||||
normalizePath(trimQuotes(this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)))
|
||||
else
|
||||
if
|
||||
this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getACommand().regexpMatch(unzipRegexp())
|
||||
this.getAFollowingStep().(Run).getScript().getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getScript().getACommand().regexpMatch(unzipRegexp())
|
||||
then result = "GITHUB_WORKSPACE/"
|
||||
else none()
|
||||
}
|
||||
@@ -213,24 +225,30 @@ class DirectArtifactDownloadStep extends UntrustedArtifactDownloadStep, Run {
|
||||
// gh api $url > "$name.zip"
|
||||
// unzip -d "$name" "$name.zip"
|
||||
// done
|
||||
this.getACommand().matches("%github.event.workflow_run.artifacts_url%") and
|
||||
this.getScript().getACommand().matches("%github.event.workflow_run.artifacts_url%") and
|
||||
(
|
||||
this.getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp())
|
||||
this.getScript().getACommand().regexpMatch(unzipRegexp()) or
|
||||
this.getAFollowingStep().(Run).getScript().getACommand().regexpMatch(unzipRegexp())
|
||||
)
|
||||
}
|
||||
|
||||
override string getPath() {
|
||||
if
|
||||
this.getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp()) or
|
||||
this.getAFollowingStep().(Run).getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
this.getScript().getACommand().regexpMatch(unzipRegexp() + unzipDirArgRegexp()) or
|
||||
this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpMatch(unzipRegexp() + unzipDirArgRegexp())
|
||||
then
|
||||
result =
|
||||
normalizePath(trimQuotes(this.getACommand()
|
||||
normalizePath(trimQuotes(this.getScript()
|
||||
.getACommand()
|
||||
.regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) or
|
||||
result =
|
||||
normalizePath(trimQuotes(this.getAFollowingStep()
|
||||
.(Run)
|
||||
.getScript()
|
||||
.getACommand()
|
||||
.regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)))
|
||||
else result = "GITHUB_WORKSPACE/"
|
||||
@@ -246,7 +264,7 @@ class ArtifactPoisoningSink extends DataFlow::Node {
|
||||
// excluding artifacts downloaded to /tmp
|
||||
not download.getPath().regexpMatch("^/tmp.*") and
|
||||
(
|
||||
poisonable.(Run).getScriptScalar() = this.asExpr() and
|
||||
poisonable.(Run).getScript() = this.asExpr() and
|
||||
(
|
||||
// Check if the poisonable step is a local script execution step
|
||||
// and the path of the command or script matches the path of the downloaded artifact
|
||||
@@ -280,7 +298,7 @@ private module ArtifactPoisoningConfig implements DataFlow::ConfigSig {
|
||||
pred instanceof ArtifactSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = step and
|
||||
(
|
||||
succ.asExpr() = step.(Run).getScriptScalar() or
|
||||
succ.asExpr() = step.(Run).getScript() or
|
||||
succ.asExpr() = step.(UsesStep)
|
||||
)
|
||||
)
|
||||
@@ -288,8 +306,8 @@ private module ArtifactPoisoningConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof ArtifactSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ private module CodeInjectionConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,8 +283,8 @@ class BashCommentVsHeadDateCheck extends CommentVsHeadDateCheck, Run {
|
||||
BashCommentVsHeadDateCheck() {
|
||||
// eg: if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
|
||||
exists(string cmd1, string cmd2 |
|
||||
cmd1 = this.getACommand() and
|
||||
cmd2 = this.getACommand() and
|
||||
cmd1 = this.getScript().getACommand() and
|
||||
cmd2 = this.getScript().getACommand() and
|
||||
not cmd1 = cmd2 and
|
||||
cmd1.toLowerCase().regexpMatch("date\\s+-d.*(commit|pushed|comment|commented)_at.*") and
|
||||
cmd2.toLowerCase().regexpMatch("date\\s+-d.*(commit|pushed|comment|commented)_at.*")
|
||||
|
||||
@@ -25,15 +25,15 @@ class EnvPathInjectionFromFileReadSink extends EnvPathInjectionSink {
|
||||
step instanceof UntrustedArtifactDownloadStep or
|
||||
step instanceof PRHeadCheckoutStep
|
||||
) and
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
this.asExpr() = run.getScript() and
|
||||
step.getAFollowingStep() = run and
|
||||
(
|
||||
exists(string cmd |
|
||||
Bash::cmdReachingGitHubFileWrite(run, cmd, "GITHUB_PATH", _) and
|
||||
Bash::outputsPartialFileContent(run, cmd)
|
||||
run.getScript().getACmdReachingGitHubPathWrite(cmd) and
|
||||
run.getScript().getAFileReadCommand() = cmd
|
||||
)
|
||||
or
|
||||
Bash::fileToGitHubPath(run, _)
|
||||
run.getScript().fileToGitHubPath(_)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -49,9 +49,8 @@ class EnvPathInjectionFromFileReadSink extends EnvPathInjectionSink {
|
||||
class EnvPathInjectionFromCommandSink extends EnvPathInjectionSink {
|
||||
EnvPathInjectionFromCommandSink() {
|
||||
exists(CommandSource source |
|
||||
this.asExpr() = source.getEnclosingRun().getScriptScalar() and
|
||||
Bash::cmdReachingGitHubFileWrite(source.getEnclosingRun(), source.getCommand(), "GITHUB_PATH",
|
||||
_)
|
||||
this.asExpr() = source.getEnclosingRun().getScript() and
|
||||
source.getEnclosingRun().getScript().getACmdReachingGitHubPathWrite(source.getCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -67,9 +66,9 @@ class EnvPathInjectionFromCommandSink extends EnvPathInjectionSink {
|
||||
class EnvPathInjectionFromEnvVarSink extends EnvPathInjectionSink {
|
||||
EnvPathInjectionFromEnvVarSink() {
|
||||
exists(Run run, string var_name |
|
||||
Bash::envReachingGitHubFileWrite(run, var_name, "GITHUB_PATH", _) and
|
||||
run.getScript().getAnEnvReachingGitHubPathWrite(var_name) and
|
||||
exists(run.getInScopeEnvVarExpr(var_name)) and
|
||||
run.getScriptScalar() = this.asExpr()
|
||||
run.getScript() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -90,8 +89,12 @@ private module EnvPathInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Run run, string var |
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::envReachingGitHubFileWrite(run, var, ["GITHUB_ENV", "GITHUB_OUTPUT", "GITHUB_PATH"], _)
|
||||
succ.asExpr() = run.getScript() and
|
||||
(
|
||||
run.getScript().getAnEnvReachingGitHubOutputWrite(var, _) or
|
||||
run.getScript().getAnEnvReachingGitHubEnvWrite(var, _) or
|
||||
run.getScript().getAnEnvReachingGitHubPathWrite(var)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Uses step |
|
||||
@@ -104,8 +107,8 @@ private module EnvPathInjectionConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,15 @@ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink {
|
||||
step instanceof UntrustedArtifactDownloadStep or
|
||||
step instanceof PRHeadCheckoutStep
|
||||
) and
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
this.asExpr() = run.getScript() and
|
||||
step.getAFollowingStep() = run and
|
||||
(
|
||||
exists(string cmd |
|
||||
Bash::cmdReachingGitHubFileWrite(run, cmd, "GITHUB_ENV", _) and
|
||||
Bash::outputsPartialFileContent(run, cmd)
|
||||
run.getScript().getACmdReachingGitHubEnvWrite(cmd, _) and
|
||||
run.getScript().getAFileReadCommand() = cmd
|
||||
)
|
||||
or
|
||||
Bash::fileToGitHubEnv(run, _)
|
||||
run.getScript().fileToGitHubEnv(_)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -52,9 +52,8 @@ class EnvVarInjectionFromFileReadSink extends EnvVarInjectionSink {
|
||||
class EnvVarInjectionFromCommandSink extends EnvVarInjectionSink {
|
||||
EnvVarInjectionFromCommandSink() {
|
||||
exists(CommandSource source |
|
||||
this.asExpr() = source.getEnclosingRun().getScriptScalar() and
|
||||
Bash::cmdReachingGitHubFileWrite(source.getEnclosingRun(), source.getCommand(), "GITHUB_ENV",
|
||||
_)
|
||||
this.asExpr() = source.getEnclosingRun().getScript() and
|
||||
source.getEnclosingRun().getScript().getACmdReachingGitHubEnvWrite(source.getCommand(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -71,8 +70,8 @@ class EnvVarInjectionFromEnvVarSink extends EnvVarInjectionSink {
|
||||
EnvVarInjectionFromEnvVarSink() {
|
||||
exists(Run run, string var_name |
|
||||
exists(run.getInScopeEnvVarExpr(var_name)) and
|
||||
run.getScriptScalar() = this.asExpr() and
|
||||
Bash::envReachingGitHubFileWrite(run, var_name, "GITHUB_ENV", _)
|
||||
run.getScript() = this.asExpr() and
|
||||
run.getScript().getAnEnvReachingGitHubEnvWrite(var_name, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -109,8 +108,12 @@ private module EnvVarInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Run run, string var |
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::envReachingGitHubFileWrite(run, var, ["GITHUB_ENV", "GITHUB_OUTPUT", "GITHUB_PATH"], _)
|
||||
succ.asExpr() = run.getScript() and
|
||||
(
|
||||
run.getScript().getAnEnvReachingGitHubEnvWrite(var, _)
|
||||
or
|
||||
run.getScript().getAnEnvReachingGitHubOutputWrite(var, _)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Uses step |
|
||||
@@ -123,8 +126,8 @@ private module EnvVarInjectionConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ abstract class OutputClobberingSink extends DataFlow::Node { }
|
||||
*/
|
||||
class OutputClobberingFromFileReadSink extends OutputClobberingSink {
|
||||
OutputClobberingFromFileReadSink() {
|
||||
exists(Run run, Step step |
|
||||
exists(Run run, Step step, string field1, string field2 |
|
||||
(
|
||||
step instanceof UntrustedArtifactDownloadStep
|
||||
or
|
||||
@@ -31,7 +31,8 @@ class OutputClobberingFromFileReadSink extends OutputClobberingSink {
|
||||
exists(Uses uses |
|
||||
step = uses and
|
||||
uses.getCallee() = "actions/checkout" and
|
||||
exists(uses.getArgument("ref"))
|
||||
exists(uses.getArgument("ref")) and
|
||||
not uses.getArgument("ref").matches("%base%")
|
||||
)
|
||||
or
|
||||
step instanceof GitMutableRefCheckout
|
||||
@@ -43,14 +44,28 @@ class OutputClobberingFromFileReadSink extends OutputClobberingSink {
|
||||
step instanceof GhSHACheckout
|
||||
) and
|
||||
step.getAFollowingStep() = run and
|
||||
this.asExpr() = run.getScriptScalar() and
|
||||
this.asExpr() = run.getScript() and
|
||||
// A write to GITHUB_OUTPUT that is not attacker-controlled
|
||||
exists(string str |
|
||||
// The output of a command that is not a file read command
|
||||
run.getScript().getACmdReachingGitHubOutputWrite(str, field1) and
|
||||
not str = run.getScript().getAFileReadCommand()
|
||||
or
|
||||
// A hard-coded string
|
||||
run.getScript().getAWriteToGitHubOutput(field1, str) and
|
||||
str.regexpMatch("[\"'0-9a-zA-Z_\\-]+")
|
||||
) and
|
||||
// A write to GITHUB_OUTPUT that is attacker-controlled
|
||||
(
|
||||
// echo "sha=$(<test-results/sha-number)" >> $GITHUB_OUTPUT
|
||||
exists(string cmd |
|
||||
Bash::cmdReachingGitHubFileWrite(run, cmd, "GITHUB_OUTPUT", _) and
|
||||
Bash::outputsPartialFileContent(run, cmd)
|
||||
run.getScript().getACmdReachingGitHubOutputWrite(cmd, field2) and
|
||||
run.getScript().getAFileReadCommand() = cmd
|
||||
)
|
||||
or
|
||||
Bash::fileToGitHubOutput(run, _)
|
||||
// cat test-results/.vars >> $GITHUB_OUTPUT
|
||||
run.getScript().fileToGitHubOutput(_) and
|
||||
field2 = "UNKNOWN"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -66,16 +81,24 @@ class OutputClobberingFromFileReadSink extends OutputClobberingSink {
|
||||
*/
|
||||
class OutputClobberingFromEnvVarSink extends OutputClobberingSink {
|
||||
OutputClobberingFromEnvVarSink() {
|
||||
exists(Run run, string var, string field |
|
||||
Bash::envReachingGitHubFileWrite(run, var, "GITHUB_OUTPUT", field) and
|
||||
// there is a different output variable in the same script
|
||||
// TODO: key2/value2 should be declared before key/value
|
||||
exists(string field2 |
|
||||
run.getAWriteToGitHubOutput(field2, _) and
|
||||
not field2 = field
|
||||
exists(Run run, string field1, string field2 |
|
||||
// A write to GITHUB_OUTPUT that is attacker-controlled
|
||||
exists(string var |
|
||||
run.getScript().getAnEnvReachingGitHubOutputWrite(var, field1) and
|
||||
exists(run.getInScopeEnvVarExpr(var)) and
|
||||
run.getScript() = this.asExpr()
|
||||
) and
|
||||
exists(run.getInScopeEnvVarExpr(var)) and
|
||||
run.getScriptScalar() = this.asExpr()
|
||||
// A write to GITHUB_OUTPUT that is not attacker-controlled
|
||||
exists(string str |
|
||||
// The output of a command that is not a file read command
|
||||
run.getScript().getACmdReachingGitHubOutputWrite(str, field2) and
|
||||
not str = run.getScript().getAFileReadCommand()
|
||||
or
|
||||
// A hard-coded string
|
||||
run.getScript().getAWriteToGitHubOutput(field2, str) and
|
||||
str.regexpMatch("[\"'0-9a-zA-Z_\\-]+")
|
||||
) and
|
||||
not field2 = field1
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -97,13 +120,18 @@ class OutputClobberingFromEnvVarSink extends OutputClobberingSink {
|
||||
* echo $BODY
|
||||
*/
|
||||
class WorkflowCommandClobberingFromEnvVarSink extends OutputClobberingSink {
|
||||
string clobbering_var;
|
||||
string clobbered_value;
|
||||
|
||||
WorkflowCommandClobberingFromEnvVarSink() {
|
||||
exists(Run run, string clobbering_line, string var_name |
|
||||
Bash::singleLineWorkflowCmd(run.getACommand(), "set-output", _, _) and
|
||||
run.getACommand() = clobbering_line and
|
||||
clobbering_line.regexpMatch(".*echo\\s+(-e\\s+)?(\"|')?\\$(\\{)?" + var_name + ".*") and
|
||||
exists(run.getInScopeEnvVarExpr(var_name)) and
|
||||
run.getScriptScalar() = this.asExpr()
|
||||
exists(Run run, string workflow_cmd_stmt, string clobbering_stmt |
|
||||
run.getScript() = this.asExpr() and
|
||||
run.getScript().getAStmt() = clobbering_stmt and
|
||||
clobbering_stmt.regexpMatch("echo\\s+(-e\\s+)?(\"|')?\\$(\\{)?" + clobbering_var + ".*") and
|
||||
exists(run.getInScopeEnvVarExpr(clobbering_var)) and
|
||||
run.getScript().getAStmt() = workflow_cmd_stmt and
|
||||
clobbered_value =
|
||||
trimQuotes(workflow_cmd_stmt.regexpCapture(".*::set-output\\s+name=.*::(.*)", 1))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -133,30 +161,35 @@ class WorkflowCommandClobberingFromEnvVarSink extends OutputClobberingSink {
|
||||
* echo "::set-output name=OUTPUT::SAFE"
|
||||
*/
|
||||
class WorkflowCommandClobberingFromFileReadSink extends OutputClobberingSink {
|
||||
string clobbering_cmd;
|
||||
|
||||
WorkflowCommandClobberingFromFileReadSink() {
|
||||
exists(Run run, string clobbering_line |
|
||||
run.getScriptScalar() = this.asExpr() and
|
||||
Bash::singleLineWorkflowCmd(run.getACommand(), "set-output", _, _) and
|
||||
run.getACommand() = clobbering_line and
|
||||
exists(Run run, string clobbering_stmt |
|
||||
run.getScript() = this.asExpr() and
|
||||
run.getScript().getAStmt() = clobbering_stmt and
|
||||
(
|
||||
// A file is read and its content is assigned to an env var that gets printed to stdout
|
||||
// A file's content is assigned to an env var that gets printed to stdout
|
||||
// - run: |
|
||||
// foo=$(<pr-id.txt)"
|
||||
// echo "${foo}"
|
||||
exists(string var_name, string value |
|
||||
run.getAnAssignment(var_name, value) and
|
||||
Bash::outputsPartialFileContent(run, trimQuotes(value)) and
|
||||
clobbering_line.regexpMatch(".*echo\\s+(-e\\s+)?(\"|')?\\$(\\{)?" + var_name + ".*")
|
||||
exists(string var, string value |
|
||||
run.getScript().getAnAssignment(var, value) and
|
||||
clobbering_cmd = run.getScript().getAFileReadCommand() and
|
||||
trimQuotes(value) = ["$(" + clobbering_cmd + ")", "`" + clobbering_cmd + "`"] and
|
||||
clobbering_stmt.regexpMatch("echo.*\\$(\\{)?" + var + ".*")
|
||||
)
|
||||
or
|
||||
// A file is read and its content is printed to stdout
|
||||
// - run: echo "foo=$(<pr-id.txt)"
|
||||
clobbering_line.regexpMatch(".*echo\\s+(-e)?\\s*(\"|')?") and
|
||||
clobbering_line.regexpMatch(["ls", Bash::partialFileContentCommand()] + "\\s.*")
|
||||
or
|
||||
// A file content is printed to stdout
|
||||
// - run: cat pr-id.txt
|
||||
clobbering_line.regexpMatch(["ls", Bash::partialFileContentCommand()] + "\\s.*")
|
||||
clobbering_cmd = run.getScript().getACommand() and
|
||||
clobbering_cmd.regexpMatch(["ls", Bash::fileReadCommand()] + "\\s.*") and
|
||||
(
|
||||
// - run: echo "foo=$(<pr-id.txt)"
|
||||
clobbering_stmt.regexpMatch("echo.*" + clobbering_cmd + ".*")
|
||||
or
|
||||
// A file content is printed to stdout
|
||||
// - run: cat pr-id.txt
|
||||
clobbering_stmt.indexOf(clobbering_cmd) = 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -181,8 +214,8 @@ private module OutputClobberingConfig implements DataFlow::ConfigSig {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Run run, string var |
|
||||
run.getInScopeEnvVarExpr(var) = pred.asExpr() and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
run.getAWriteToGitHubOutput(_, _)
|
||||
succ.asExpr() = run.getScript() and
|
||||
run.getScript().getAWriteToGitHubOutput(_, _)
|
||||
)
|
||||
or
|
||||
exists(Uses step |
|
||||
@@ -195,10 +228,10 @@ private module OutputClobberingConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
succ.asExpr() = run.getScript() and
|
||||
(
|
||||
Bash::outputsPartialFileContent(run, run.getACommand()) or
|
||||
Bash::singleLineWorkflowCmd(run.getACommand(), "set-output", _, _)
|
||||
exists(run.getScript().getAFileReadCommand()) or
|
||||
run.getScript().getAStmt().matches("%::set-output %")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class PoisonableCommandStep extends PoisonableStep, Run {
|
||||
PoisonableCommandStep() {
|
||||
exists(string regexp |
|
||||
poisonableCommandsDataModel(regexp) and
|
||||
this.getACommand().regexpMatch("^" + regexp + ".*")
|
||||
this.getScript().getACommand().regexpMatch("^" + regexp + ".*")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class LocalScriptExecutionRunStep extends PoisonableStep, Run {
|
||||
string path;
|
||||
|
||||
LocalScriptExecutionRunStep() {
|
||||
exists(string cmd, string regexp, int path_group | cmd = this.getACommand() |
|
||||
exists(string cmd, string regexp, int path_group | cmd = this.getScript().getACommand() |
|
||||
poisonableLocalScriptsDataModel(regexp, path_group) and
|
||||
path = cmd.regexpCapture(regexp, path_group)
|
||||
)
|
||||
@@ -58,12 +58,3 @@ class LocalScriptExecutionRunStep extends PoisonableStep, Run {
|
||||
class LocalActionUsesStep extends PoisonableStep, UsesStep {
|
||||
LocalActionUsesStep() { this.getCallee().matches("./%") }
|
||||
}
|
||||
|
||||
class EnvVarInjectionFromFileReadRunStep extends PoisonableStep, Run {
|
||||
string value;
|
||||
|
||||
EnvVarInjectionFromFileReadRunStep() {
|
||||
this.getAWriteToGitHubEnv(_, value) and
|
||||
Bash::outputsPartialFileContent(this, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ private module ActionsMutableRefCheckoutConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -107,8 +107,8 @@ private module ActionsSHACheckoutConfig implements DataFlow::ConfigSig {
|
||||
exists(Run run |
|
||||
pred instanceof FileSource and
|
||||
pred.asExpr().(Step).getAFollowingStep() = run and
|
||||
succ.asExpr() = run.getScriptScalar() and
|
||||
Bash::outputsPartialFileContent(run, run.getACommand())
|
||||
succ.asExpr() = run.getScript() and
|
||||
exists(run.getScript().getAFileReadCommand())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ class ActionsSHACheckout extends SHACheckoutStep instanceof UsesStep {
|
||||
/** Checkout of a Pull Request HEAD ref using git within a Run step */
|
||||
class GitMutableRefCheckout extends MutableRefCheckoutStep instanceof Run {
|
||||
GitMutableRefCheckout() {
|
||||
exists(string cmd | this.getACommand() = cmd |
|
||||
exists(string cmd | this.getScript().getACommand() = cmd |
|
||||
cmd.regexpMatch("git\\s+(fetch|pull).*") and
|
||||
(
|
||||
(containsHeadRef(cmd) or containsPullRequestNumber(cmd))
|
||||
@@ -306,7 +306,7 @@ class GitMutableRefCheckout extends MutableRefCheckoutStep instanceof Run {
|
||||
/** Checkout of a Pull Request HEAD ref using git within a Run step */
|
||||
class GitSHACheckout extends SHACheckoutStep instanceof Run {
|
||||
GitSHACheckout() {
|
||||
exists(string cmd | this.getACommand() = cmd |
|
||||
exists(string cmd | this.getScript().getACommand() = cmd |
|
||||
cmd.regexpMatch("git\\s+(fetch|pull).*") and
|
||||
(
|
||||
containsHeadSHA(cmd)
|
||||
@@ -326,7 +326,7 @@ class GitSHACheckout extends SHACheckoutStep instanceof Run {
|
||||
/** Checkout of a Pull Request HEAD ref using gh within a Run step */
|
||||
class GhMutableRefCheckout extends MutableRefCheckoutStep instanceof Run {
|
||||
GhMutableRefCheckout() {
|
||||
exists(string cmd | this.getACommand() = cmd |
|
||||
exists(string cmd | this.getScript().getACommand() = cmd |
|
||||
cmd.regexpMatch(".*(gh|hub)\\s+pr\\s+checkout.*") and
|
||||
(
|
||||
(containsHeadRef(cmd) or containsPullRequestNumber(cmd))
|
||||
@@ -348,7 +348,7 @@ class GhMutableRefCheckout extends MutableRefCheckoutStep instanceof Run {
|
||||
/** Checkout of a Pull Request HEAD ref using gh within a Run step */
|
||||
class GhSHACheckout extends SHACheckoutStep instanceof Run {
|
||||
GhSHACheckout() {
|
||||
exists(string cmd | this.getACommand() = cmd |
|
||||
exists(string cmd | this.getScript().getACommand() = cmd |
|
||||
cmd.regexpMatch("gh\\s+pr\\s+checkout.*") and
|
||||
(
|
||||
containsHeadSHA(cmd)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
on: push
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -wkf
|
||||
|
||||
jobs:
|
||||
local_commands:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -job
|
||||
steps:
|
||||
- run: |
|
||||
|
||||
- shell: bash -step
|
||||
run: |
|
||||
command1 ; command2
|
||||
- run: |
|
||||
command3 | command4
|
||||
@@ -19,3 +28,12 @@ jobs:
|
||||
- run: |
|
||||
command13 "`command14` $(date | wc -l)"
|
||||
|
||||
local_commands2:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- shell: bash -step
|
||||
run: |
|
||||
command1 ; command2
|
||||
- shell: pwsh
|
||||
run: |
|
||||
command3 | command4
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
| .github/workflows/commands.yml:7:9:9:6 | Run Step | command1 |
|
||||
| .github/workflows/commands.yml:7:9:9:6 | Run Step | command2 |
|
||||
| .github/workflows/commands.yml:9:9:11:6 | Run Step | command3 |
|
||||
| .github/workflows/commands.yml:9:9:11:6 | Run Step | command4 |
|
||||
| .github/workflows/commands.yml:11:9:13:6 | Run Step | command5 "$(command6)" |
|
||||
| .github/workflows/commands.yml:11:9:13:6 | Run Step | command6 |
|
||||
| .github/workflows/commands.yml:13:9:15:6 | Run Step | command7 |
|
||||
| .github/workflows/commands.yml:13:9:15:6 | Run Step | command8 |
|
||||
| .github/workflows/commands.yml:15:9:17:6 | Run Step | command9 |
|
||||
| .github/workflows/commands.yml:15:9:17:6 | Run Step | command10 |
|
||||
| .github/workflows/commands.yml:17:9:19:6 | Run Step | command11 "`command12`" |
|
||||
| .github/workflows/commands.yml:17:9:19:6 | Run Step | command12 |
|
||||
| .github/workflows/commands.yml:19:9:20:50 | Run Step | command13 "`command14` $(date \| wc -l)" |
|
||||
| .github/workflows/commands.yml:19:9:20:50 | Run Step | command14 |
|
||||
| .github/workflows/commands.yml:19:9:20:50 | Run Step | date |
|
||||
| .github/workflows/commands.yml:19:9:20:50 | Run Step | wc -l |
|
||||
| .github/workflows/commands.yml:15:9:18:6 | Run Step | command1 |
|
||||
| .github/workflows/commands.yml:15:9:18:6 | Run Step | command2 |
|
||||
| .github/workflows/commands.yml:18:9:20:6 | Run Step | command3 |
|
||||
| .github/workflows/commands.yml:18:9:20:6 | Run Step | command4 |
|
||||
| .github/workflows/commands.yml:20:9:22:6 | Run Step | command5 "$(command6)" |
|
||||
| .github/workflows/commands.yml:20:9:22:6 | Run Step | command6 |
|
||||
| .github/workflows/commands.yml:22:9:24:6 | Run Step | command7 |
|
||||
| .github/workflows/commands.yml:22:9:24:6 | Run Step | command8 |
|
||||
| .github/workflows/commands.yml:24:9:26:6 | Run Step | command9 |
|
||||
| .github/workflows/commands.yml:24:9:26:6 | Run Step | command10 |
|
||||
| .github/workflows/commands.yml:26:9:28:6 | Run Step | command11 "`command12`" |
|
||||
| .github/workflows/commands.yml:26:9:28:6 | Run Step | command12 |
|
||||
| .github/workflows/commands.yml:28:9:31:2 | Run Step | command13 "`command14` $(date \| wc -l)" |
|
||||
| .github/workflows/commands.yml:28:9:31:2 | Run Step | command14 |
|
||||
| .github/workflows/commands.yml:28:9:31:2 | Run Step | date |
|
||||
| .github/workflows/commands.yml:28:9:31:2 | Run Step | wc -l |
|
||||
| .github/workflows/commands.yml:34:9:37:6 | Run Step | command1 |
|
||||
| .github/workflows/commands.yml:34:9:37:6 | Run Step | command2 |
|
||||
| .github/workflows/expression_nodes.yml:7:9:8:6 | Run Step | LINE 1echo '${{ github.event.comment.body }}' |
|
||||
| .github/workflows/expression_nodes.yml:8:9:10:6 | Run Step | LINE 1 echo '${{ github.event.comment.body }}' |
|
||||
| .github/workflows/expression_nodes.yml:10:9:13:6 | Run Step | LINE 1 echo '${{ github.event.comment.body }}' |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import actions
|
||||
|
||||
from Run run
|
||||
select run, run.getACommand()
|
||||
select run, run.getScript().getACommand()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
| .github/workflows/multiline2.yml:63:9:66:6 | Run Step |
|
||||
| .github/workflows/multiline.yml:63:9:66:6 | Run Step |
|
||||
| .github/workflows/poisonable_steps.yml:8:9:13:6 | Uses Step |
|
||||
| .github/workflows/poisonable_steps.yml:13:9:14:6 | Run Step |
|
||||
| .github/workflows/poisonable_steps.yml:14:9:15:6 | Run Step |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
ERROR: Ast::ShellScript is incompatible with string (test.ql:24,66-67)
|
||||
ERROR: getCallee() cannot be resolved for type DataFlowPublic::CallNode (test.ql:62,79-88)
|
||||
|
||||
@@ -34,5 +34,6 @@ jobs:
|
||||
name: pr_number
|
||||
- id: clob1
|
||||
run: |
|
||||
# VULNERABLE
|
||||
echo "OUTPUT_1=HARDCODED" >> $GITHUB_OUTPUT
|
||||
echo "OUTPUT_2=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
|
||||
32
ql/test/query-tests/Security/CWE-077/.github/actions/download-artifact-2/action.yaml
vendored
Normal file
32
ql/test/query-tests/Security/CWE-077/.github/actions/download-artifact-2/action.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: DownloadArtifacts
|
||||
description: 'Downloads and unarchives artifacts for a workflow that runs on workflow_run so that it can use its data'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
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 == "artifacts"
|
||||
})[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(`/tmp/artifacts.zip`, Buffer.from(download.data));
|
||||
- run: |
|
||||
mkdir -p /tmp/artifacts
|
||||
unzip /tmp/artifacts.zip
|
||||
shell: bash
|
||||
- run: |
|
||||
echo "Downloaded artifacts:"
|
||||
ls -ablh
|
||||
shell: bash
|
||||
32
ql/test/query-tests/Security/CWE-077/.github/actions/download-artifact/action.yaml
vendored
Normal file
32
ql/test/query-tests/Security/CWE-077/.github/actions/download-artifact/action.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: DownloadArtifacts
|
||||
description: 'Downloads and unarchives artifacts for a workflow that runs on workflow_run so that it can use its data'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
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 == "artifacts"
|
||||
})[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(`/tmp/artifacts.zip`, Buffer.from(download.data));
|
||||
- run: |
|
||||
mkdir -p /tmp/artifacts
|
||||
unzip /tmp/artifacts.zip -d /tmp/artifacts
|
||||
shell: bash
|
||||
- run: |
|
||||
echo "Downloaded artifacts:"
|
||||
ls -ablh /tmp/artifacts
|
||||
shell: bash
|
||||
20
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning51.yml
vendored
Normal file
20
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning51.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Pull Request Open
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Prev"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
Download:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
gh run download "${{github.event.workflow_run.id}}" --repo "${GITHUB_REPOSITORY}" --name "artifact_name"
|
||||
- name: Unzip
|
||||
run: |
|
||||
unzip artifact_name.zip -d foo
|
||||
- name: Env Var Injection
|
||||
run: |
|
||||
echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV
|
||||
26
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning52.yml
vendored
Normal file
26
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning52.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Pull Request Open
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Prev"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
Download:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
gh run download "${{github.event.workflow_run.id}}" --repo "${GITHUB_REPOSITORY}" --name "artifact_name"
|
||||
- name: Unzip
|
||||
run: |
|
||||
unzip artifact_name.zip -d foo
|
||||
- name: Env Var Injection
|
||||
run: |
|
||||
echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"
|
||||
cat foo >> "$GITHUB_ENV"
|
||||
echo "EOF" >> "${GITHUB_ENV}"
|
||||
|
||||
|
||||
|
||||
|
||||
27
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning53.yml
vendored
Normal file
27
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning53.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Pull Request Open
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Prev"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
Download:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
gh run download "${{github.event.workflow_run.id}}" --repo "${GITHUB_REPOSITORY}" --name "artifact_name"
|
||||
- name: Unzip
|
||||
run: |
|
||||
unzip artifact_name.zip -d foo
|
||||
- run: |
|
||||
{
|
||||
echo 'JSON_RESPONSE<<EOF'
|
||||
cat foo
|
||||
echo EOF
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
|
||||
|
||||
|
||||
29
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning91.yml
vendored
Normal file
29
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning91.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: SnapshotPR
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- ApprovalComment
|
||||
types:
|
||||
- completed
|
||||
jobs:
|
||||
snapshot:
|
||||
permissions:
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
- uses: ./.github/actions/download-artifact
|
||||
- id: metadata
|
||||
run: |
|
||||
pr_number="$(head -n 2 /tmp/artifacts/metadata.txt | tail -n 1)"
|
||||
pr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"
|
||||
echo PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"
|
||||
echo PR_NUMBER="$pr_number" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
with:
|
||||
ref: ${{ env.PR_COMMIT }}
|
||||
- uses: ./.github/actions/install-deps
|
||||
- run: make snapshot
|
||||
29
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning92.yml
vendored
Normal file
29
ql/test/query-tests/Security/CWE-077/.github/workflows/artifactpoisoning92.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: SnapshotPR
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- ApprovalComment
|
||||
types:
|
||||
- completed
|
||||
jobs:
|
||||
snapshot:
|
||||
permissions:
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
- uses: ./.github/actions/download-artifact-2
|
||||
- id: metadata
|
||||
run: |
|
||||
pr_number="$(head -n 2 /tmp/artifacts/metadata.txt | tail -n 1)"
|
||||
pr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"
|
||||
echo PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"
|
||||
echo PR_NUMBER="$pr_number" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
with:
|
||||
ref: ${{ env.PR_COMMIT }}
|
||||
- uses: ./.github/actions/install-deps
|
||||
- run: make snapshot
|
||||
@@ -1,4 +1,9 @@
|
||||
edges
|
||||
| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/actions/download-artifact/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .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 | provenance | Config |
|
||||
| .github/workflows/test3.yml:13:7:20:4 | Uses Step | .github/workflows/test3.yml:20:12:23:77 | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/test4.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test4.yml:12:14:13:48 | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n | provenance | Config |
|
||||
@@ -25,6 +30,16 @@ edges
|
||||
| .github/workflows/test12.yml:38:9:46:6 | Uses Step | .github/workflows/test12.yml:63:14:68:29 | {\n echo 'PRERELEASE_REPORT<<EOF'\n cat prerelease-report.md\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/test12.yml:55:9:61:6 | Uses Step | .github/workflows/test12.yml:63:14:68:29 | {\n echo 'PRERELEASE_REPORT<<EOF'\n cat prerelease-report.md\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
nodes
|
||||
| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/actions/download-artifact/action.yaml:6:7:25:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | semmle.label | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | semmle.label | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | semmle.label | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | semmle.label | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | semmle.label | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .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 |
|
||||
| .github/workflows/test3.yml:13:7:20:4 | Uses Step | semmle.label | Uses Step |
|
||||
@@ -79,6 +94,11 @@ nodes
|
||||
| .github/workflows/test15.yml:18:14:20:48 | PR_BODY=$(jq --raw-output .pull_request.body ${GITHUB_EVENT_PATH})\necho "BODY=$PR_BODY" >> "$GITHUB_ENV"\n | semmle.label | PR_BODY=$(jq --raw-output .pull_request.body ${GITHUB_EVENT_PATH})\necho "BODY=$PR_BODY" >> "$GITHUB_ENV"\n |
|
||||
subpaths
|
||||
#select
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | .github/actions/download-artifact/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .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 |
|
||||
| .github/workflows/test3.yml:20:12:23:77 | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n | .github/workflows/test3.yml:13:7:20:4 | Uses Step | .github/workflows/test3.yml:20:12:23:77 | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test3.yml:20:12:23:77 | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/test4.yml:12:14:13:48 | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n | .github/workflows/test4.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test4.yml:12:14:13:48 | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n | Potential environment variable injection in $@, which may be controlled by an external user. | .github/workflows/test4.yml:12:14:13:48 | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n |
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
edges
|
||||
| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/actions/download-artifact/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .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 | provenance | Config |
|
||||
| .github/workflows/test3.yml:13:7:20:4 | Uses Step | .github/workflows/test3.yml:20:12:23:77 | echo "PR_NUMBER=$(cat pr_number.txt \| jq -r .)" >> $GITHUB_ENV\necho "PR_HEAD_REPO=$(cat pr_head_repo.txt \| jq -Rr .)" >> $GITHUB_ENV\necho "PR_HEAD_REF=$(cat pr_head_ref.txt \| jq -Rr .)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/test4.yml:11:19:11:56 | github.event.pull_request.title | .github/workflows/test4.yml:12:14:13:48 | echo "PR_TITLE=$TITLE" >> $GITHUB_ENV\n | provenance | Config |
|
||||
@@ -25,6 +30,16 @@ edges
|
||||
| .github/workflows/test12.yml:38:9:46:6 | Uses Step | .github/workflows/test12.yml:63:14:68:29 | {\n echo 'PRERELEASE_REPORT<<EOF'\n cat prerelease-report.md\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/test12.yml:55:9:61:6 | Uses Step | .github/workflows/test12.yml:63:14:68:29 | {\n echo 'PRERELEASE_REPORT<<EOF'\n cat prerelease-report.md\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
nodes
|
||||
| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/actions/download-artifact/action.yaml:6:7:25:4 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | semmle.label | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | semmle.label | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | semmle.label | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning91.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | semmle.label | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning92.yml:20:14:24:55 | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n | semmle.label | pr_number="$(head -n 2 /tmp/artifacts/metadata.txt \| tail -n 1)"\npr_commit="$(tail -n 1 /tmp/artifacts/metadata.txt)"\necho PR_COMMIT="$pr_commit" >> "$GITHUB_ENV"\necho PR_NUMBER="$pr_number" >> "$GITHUB_ENV"\n |
|
||||
| .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 |
|
||||
| .github/workflows/test3.yml:13:7:20:4 | Uses Step | semmle.label | Uses Step |
|
||||
|
||||
@@ -11,9 +11,6 @@ edges
|
||||
| .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | provenance | Config |
|
||||
nodes
|
||||
@@ -38,12 +35,6 @@ nodes
|
||||
| .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | semmle.label | ./foo/cmd |
|
||||
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | semmle.label | ./cmd |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | semmle.label | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | semmle.label | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | semmle.label | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | semmle.label | sed -f config foo.md > bar.md\n |
|
||||
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | semmle.label | Uses Step |
|
||||
@@ -62,9 +53,6 @@ subpaths
|
||||
| .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | npm install\nnpm run lint\n |
|
||||
| .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | ./foo/cmd |
|
||||
| .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | ./cmd |
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | sed -f config foo.md > bar.md\n |
|
||||
| .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | python test.py |
|
||||
| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Potential artifact poisoning in $@, which may be controlled by an external user. | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Uses Step |
|
||||
|
||||
@@ -11,9 +11,6 @@ edges
|
||||
| .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | provenance | Config |
|
||||
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | provenance | Config |
|
||||
nodes
|
||||
@@ -38,12 +35,6 @@ nodes
|
||||
| .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | semmle.label | ./foo/cmd |
|
||||
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | semmle.label | ./cmd |
|
||||
| .github/workflows/artifactpoisoning51.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning51.yml:19:14:20:57 | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n | semmle.label | echo "pr_number=$(cat foo/bar)" >> $GITHUB_ENV\n |
|
||||
| .github/workflows/artifactpoisoning52.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning52.yml:19:14:22:40 | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n | semmle.label | echo "PACKAGES_FILE_LIST<<EOF" >> "${GITHUB_ENV}"\ncat foo >> "$GITHUB_ENV"\necho "EOF" >> "${GITHUB_ENV}"\n |
|
||||
| .github/workflows/artifactpoisoning53.yml:13:9:15:6 | Run Step | semmle.label | Run Step |
|
||||
| .github/workflows/artifactpoisoning53.yml:18:14:23:29 | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n | semmle.label | {\n echo 'JSON_RESPONSE<<EOF'\n cat foo\n echo EOF\n} >> "$GITHUB_ENV"\n |
|
||||
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | semmle.label | Uses Step |
|
||||
| .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | semmle.label | sed -f config foo.md > bar.md\n |
|
||||
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | semmle.label | Uses Step |
|
||||
|
||||
Reference in New Issue
Block a user