Skip to content

Commit 83ca36b

Browse files
author
Alvaro Muñoz
committed
Support RunExpr's env vars
1 parent 1708e0f commit 83ca36b

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

ql/lib/codeql/actions/Ast.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ class RunExpr extends StepStmt, Expression {
169169

170170
Expression getScriptExpr() { result = scriptExpr }
171171

172+
Expression getEnvExpr(string name) {
173+
exists(Actions::StepEnv env |
174+
env.getStep() = this and
175+
env.(YamlMapping).maps(any(YamlScalar s | s.getValue() = name), result)
176+
)
177+
}
178+
172179
string getScript() { result = scriptExpr.getValue() }
173180
}
174181

@@ -183,6 +190,7 @@ class ExprAccessExpr extends Expression instanceof YamlString {
183190
string getExpression() { result = expr }
184191

185192
JobStmt getJob() { result.getAChildNode*() = this }
193+
//override string toString() { result = expr }
186194
}
187195

188196
/**

ql/lib/codeql/actions/controlflow/internal/Cfg.qll

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,18 @@ private class UsesTree extends StandardPreOrderTree instanceof UsesExpr {
171171
}
172172

173173
private class RunTree extends StandardPreOrderTree instanceof RunExpr {
174-
override ControlFlowTree getChildNode(int i) { result = super.getScriptExpr() and i = 0 }
174+
//override ControlFlowTree getChildNode(int i) { result = super.getScriptExpr() and i = 0 }
175+
override ControlFlowTree getChildNode(int i) {
176+
result =
177+
rank[i](Expression child, Location l |
178+
(child = super.getEnvExpr(_) or child = super.getScriptExpr()) and
179+
l = child.getLocation()
180+
|
181+
child
182+
order by
183+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
184+
)
185+
}
175186
}
176187

177188
private class ExprAccessTree extends LeafTree instanceof ExprAccessExpr { }
178-

ql/lib/codeql/actions/dataflow/FlowSteps.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class AdditionalTaintStep extends Unit {
2020
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
2121
}
2222

23+
/**
24+
* Holds if actions-find-and-replace-string step is used.
25+
*/
2326
private class ActionsFindAndReplaceStringStep extends AdditionalTaintStep {
2427
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
2528
exists(UsesExpr u |
@@ -29,3 +32,32 @@ private class ActionsFindAndReplaceStringStep extends AdditionalTaintStep {
2932
)
3033
}
3134
}
35+
36+
/**
37+
* Holds if a Run step declares an environment variable, uses it in its script and sets an output in its script.
38+
* e.g.
39+
* - name: Extract and Clean Initial URL
40+
* id: extract-url
41+
* env:
42+
* BODY: ${{ github.event.comment.body }}
43+
* run: |
44+
* INITIAL_URL=$(echo "$BODY" | grep -o 'https://github.com/github/release-assets/assets/[^ >]*')
45+
* echo "Cleaned Initial URL: $INITIAL_URL"
46+
* echo "::set-output name=initial_url::$INITIAL_URL"
47+
*/
48+
private class RunEnvToScriptStep extends AdditionalTaintStep {
49+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { test(pred, succ) }
50+
}
51+
52+
predicate test(DataFlow::Node pred, DataFlow::Node succ) {
53+
exists(RunExpr r, string varName |
54+
r.getEnvExpr(varName) = pred.asExpr() and
55+
exists(string script, string line |
56+
script = r.getScript() and
57+
line = script.splitAt("\n") and
58+
line.regexpMatch(".*::set-output\\s+name.*") and
59+
script.indexOf("$" + ["", "{", "ENV{"] + varName) > 0
60+
) and
61+
succ.asExpr() = r
62+
)
63+
}

ql/lib/codeql/actions/dataflow/internal/DataFlowPrivate.qll

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos =
197197
*/
198198
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { localFlowStep(nodeFrom, nodeTo) }
199199

200-
predicate stepOutputDefToUse(Node nodeFrom, Node nodeTo) {
200+
predicate usesOutputDefToUse(Node nodeFrom, Node nodeTo) {
201201
// nodeTo is an OutputVarAccessExpr scoped with the namespace of the nodeFrom Step output
202202
exists(UsesExpr uses, StepOutputAccessExpr outputRead |
203203
uses = nodeFrom.asExpr() and
@@ -207,6 +207,16 @@ predicate stepOutputDefToUse(Node nodeFrom, Node nodeTo) {
207207
)
208208
}
209209

210+
predicate runOutputDefToUse(Node nodeFrom, Node nodeTo) {
211+
// nodeTo is an OutputVarAccessExpr scoped with the namespace of the nodeFrom Step output
212+
exists(RunExpr uses, StepOutputAccessExpr outputRead |
213+
uses = nodeFrom.asExpr() and
214+
outputRead = nodeTo.asExpr() and
215+
outputRead.getStepId() = uses.getId() and
216+
uses.getJob() = outputRead.getJob()
217+
)
218+
}
219+
210220
predicate jobOutputDefToUse(Node nodeFrom, Node nodeTo) {
211221
// nodeTo is a JobOutputAccessExpr and nodeFrom is the Job output expression
212222
exists(Expression astFrom, JobOutputAccessExpr astTo |
@@ -223,7 +233,8 @@ predicate jobOutputDefToUse(Node nodeFrom, Node nodeTo) {
223233
*/
224234
pragma[nomagic]
225235
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
226-
stepOutputDefToUse(nodeFrom, nodeTo) or
236+
usesOutputDefToUse(nodeFrom, nodeTo) or
237+
runOutputDefToUse(nodeFrom, nodeTo) or
227238
jobOutputDefToUse(nodeFrom, nodeTo)
228239
}
229240

0 commit comments

Comments
 (0)