Skip to content

Commit 0398fbd

Browse files
author
Alvaro Muñoz
committed
Refactor AST layer
1 parent b3eae71 commit 0398fbd

File tree

4 files changed

+142
-156
lines changed

4 files changed

+142
-156
lines changed

ql/lib/codeql/actions/Ast.qll

Lines changed: 92 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,74 @@
11
private import codeql.actions.ast.internal.Actions
22
private import codeql.Locations
33

4+
/**
5+
* Base class for the AST tree.
6+
* Based on YamlNode from the Yaml library but making mapping values children of the mapping keys:
7+
* eg: top:
8+
* key: value
9+
* According to the Yaml library, both `key` and `value` are direct children of `top`
10+
* This Tree implementation makes `key` child od `top` and `value` child of `key`
11+
*/
412
class AstNode instanceof YamlNode {
5-
AstNode getParentNode() {
6-
if exists(YamlMapping m | m.maps(_, this))
7-
then exists(YamlMapping m | m.maps(result, this))
8-
else result = super.getParentNode()
9-
}
10-
11-
AstNode getAChildNode() {
12-
if this instanceof YamlMapping
13-
then this.(YamlMapping).maps(result, _)
14-
else
15-
if this instanceof YamlCollection
16-
then result = super.getChildNode(_)
17-
else
18-
if this instanceof YamlScalar and exists(YamlMapping m | m.maps(this, _))
19-
then exists(YamlMapping m | m.maps(this, result))
20-
else none()
21-
}
22-
23-
AstNode getChildNodeByOrder(int i) {
24-
result =
25-
rank[i](Expression child, Location l |
26-
child = this.getAChildNode() and
27-
child.getLocation() = l
28-
|
29-
child
30-
order by
31-
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
32-
)
33-
}
34-
13+
AstNode getParentNode() { result = super.getParentNode() }
14+
15+
// AstNode getParentNode() {
16+
// if exists(YamlMapping m | m.maps(_, this))
17+
// then exists(YamlMapping m | m.maps(result, this))
18+
// else result = super.getParentNode()
19+
// }
20+
AstNode getAChildNode() { result = super.getAChildNode() }
21+
22+
// AstNode getAChildNode() {
23+
// if this instanceof YamlMapping
24+
// then this.(YamlMapping).maps(result, _)
25+
// else
26+
// if this instanceof YamlCollection
27+
// then result = super.getChildNode(_)
28+
// else
29+
// if this instanceof YamlScalar and exists(YamlMapping m | m.maps(this, _))
30+
// then exists(YamlMapping m | m.maps(this, result))
31+
// else none()
32+
// }
33+
// /**
34+
// * This should be getAChildNode(int i)
35+
// */
36+
// AstNode getChildNodeByOrder(int i) {
37+
// result =
38+
// rank[i](Expression child, Location l |
39+
// child = this.getAChildNode() and
40+
// child.getLocation() = l
41+
// |
42+
// child
43+
// order by
44+
// l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
45+
// )
46+
// }
3547
string toString() { result = super.toString() }
3648

3749
string getAPrimaryQlClass() { result = super.getAPrimaryQlClass() }
3850

3951
Location getLocation() { result = super.getLocation() }
4052
}
4153

42-
class Statement extends AstNode {
43-
// narrow down to something that is a statement
44-
// A statement is a group of expressions and/or statements that you design to carry out a task or an action.
45-
// Any statement that can return a value is automatically qualified to be used as an expression.
46-
}
54+
/**
55+
* A statement is a group of expressions and/or statements that you design to carry out a task or an action.
56+
* Any statement that can return a value is automatically qualified to be used as an expression.
57+
*/
58+
class Statement extends AstNode { }
59+
60+
/**
61+
* An expression is any word or group of words or symbols that is a value. In programming, an expression is a value, or anything that executes and ends up being a value.
62+
*/
63+
class Expression extends Statement { }
64+
65+
/**
66+
* A Github Actions Workflow
67+
*/
68+
class WorkflowStmt extends Statement instanceof Actions::Workflow {
69+
JobStmt getAJob() { result = super.getJob(_) }
4770

48-
class Expression extends Statement {
49-
// narrow down to something that is an expression
50-
// An expression is any word or group of words or symbols that is a value. In programming, an expression is a value, or anything that executes and ends up being a value.
71+
JobStmt getJob(string id) { result = super.getJob(id) }
5172
}
5273

5374
/**
@@ -60,54 +81,53 @@ class JobStmt extends Statement instanceof Actions::Job {
6081
*/
6182
string getId() { result = super.getId() }
6283

63-
/** Gets the human-readable name of this job, if any, as a string. */
64-
string getName() {
65-
result = super.getId()
66-
or
67-
not exists(string s | s = super.getId()) and result = "unknown"
68-
}
69-
7084
/** Gets the step at the given index within this job. */
7185
StepStmt getStep(int index) { result = super.getStep(index) }
7286

7387
/** Gets any steps that are defined within this job. */
7488
StepStmt getAStep() { result = super.getStep(_) }
7589

90+
/**
91+
* Gets a needed job.
92+
* eg:
93+
* - needs: [job1, job2]
94+
*/
7695
JobStmt getNeededJob() {
7796
exists(Actions::Needs needs |
7897
needs.getJob() = this and
7998
result = needs.getANeededJob().(JobStmt)
8099
)
81100
}
82101

83-
Expression getJobOutputExpr(string varName) {
84-
this.(Actions::Job)
85-
.lookup("outputs")
86-
.(YamlMapping)
87-
.maps(any(YamlScalar a | a.getValue() = varName), result)
88-
}
89-
90-
JobOutputStmt getJobOutputStmt() { result = this.(Actions::Job).lookup("outputs") }
91-
92-
Statement getSuccNode(int i) {
93-
result =
94-
rank[i](Expression child, Location l |
95-
(child = this.getAStep() or child = this.getJobOutputStmt()) and
96-
l = child.getLocation()
97-
|
98-
child
99-
order by
100-
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
101-
)
102-
}
102+
/**
103+
* Gets the declaration of the outputs for the job.
104+
* eg:
105+
* out1: ${steps.foo.bar}
106+
* out2: ${steps.foo.baz}
107+
*/
108+
JobOutputStmt getOutputStmt() { result = this.(Actions::Job).lookup("outputs") }
103109
}
104110

111+
/**
112+
* Declaration of the outputs for the job.
113+
* eg:
114+
* out1: ${steps.foo.bar}
115+
* out2: ${steps.foo.baz}
116+
*/
105117
class JobOutputStmt extends Statement instanceof YamlMapping {
106118
JobStmt job;
107119

108120
JobOutputStmt() { job.(YamlMapping).lookup("outputs") = this }
109121

110-
StepOutputAccessExpr getSuccNode(int i) { result = this.(YamlMapping).getValueNode(i) }
122+
YamlMapping asYamlMapping() { result = this }
123+
124+
/**
125+
* Gets a specific value expression
126+
* eg: ${steps.foo.bar}
127+
*/
128+
Expression getOutputExpr(string id) {
129+
this.(YamlMapping).maps(any(YamlScalar s | s.getValue() = id), result)
130+
}
111131
}
112132

113133
/**
@@ -116,15 +136,7 @@ class JobOutputStmt extends Statement instanceof YamlMapping {
116136
class StepStmt extends Statement instanceof Actions::Step {
117137
string getId() { result = super.getId() }
118138

119-
string getName() {
120-
result = super.getId()
121-
or
122-
not exists(string s | s = super.getId()) and result = "unknown"
123-
}
124-
125139
JobStmt getJob() { result = super.getJob() }
126-
127-
abstract AstNode getSuccNode(int i);
128140
}
129141

130142
/**
@@ -145,57 +157,23 @@ class UsesExpr extends StepStmt, Expression {
145157
result = with.lookup(key)
146158
)
147159
}
148-
149-
Expression getArgumentByOrder(int i) {
150-
exists(Actions::With with |
151-
with.getStep() = uses.getStep() and
152-
result =
153-
rank[i](Expression child, Location l |
154-
child = with.lookup(_) and l = child.getLocation()
155-
|
156-
child
157-
order by
158-
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
159-
)
160-
)
161-
}
162-
163-
Expression getAnArgument() {
164-
exists(Actions::With with |
165-
with.getStep() = this and
166-
result = with.lookup(_)
167-
)
168-
}
169-
170-
override AstNode getSuccNode(int i) { result = this.getArgumentByOrder(i) }
171160
}
172161

173162
/**
174-
* An argument passed to a UsesExpr.
163+
* A Run step represents the evaluation of a provided script
175164
*/
176-
class ArgumentExpr extends Expression {
177-
UsesExpr uses;
178-
179-
ArgumentExpr() { this = uses.getAnArgument() }
180-
}
181-
182-
/**
183-
* A Run step represents a call to an inline script or executable on the runner machine.
184-
*/
185-
class RunExpr extends StepStmt {
165+
class RunExpr extends StepStmt, Expression {
186166
Actions::Run scriptExpr;
187167

188168
RunExpr() { scriptExpr.getStep() = this }
189169

190170
Expression getScriptExpr() { result = scriptExpr }
191171

192172
string getScript() { result = scriptExpr.getValue() }
193-
194-
override AstNode getSuccNode(int i) { result = this.getScriptExpr() and i = 0 }
195173
}
196174

197175
/**
198-
* A YAML string containing a workflow expression.
176+
* Evaluation of a workflow expression ${{}}.
199177
*/
200178
class ExprAccessExpr extends Expression instanceof YamlString {
201179
string expr;
@@ -208,7 +186,7 @@ class ExprAccessExpr extends Expression instanceof YamlString {
208186
}
209187

210188
/**
211-
* A ExprAccessExpr where the expression references a step output.
189+
* A ExprAccessExpr where the expression evaluated is a step output read.
212190
* eg: `${{ steps.changed-files.outputs.all_changed_files }}`
213191
*/
214192
class StepOutputAccessExpr extends ExprAccessExpr {
@@ -230,7 +208,7 @@ class StepOutputAccessExpr extends ExprAccessExpr {
230208
}
231209

232210
/**
233-
* A ExprAccessExpr where the expression references a job output.
211+
* A ExprAccessExpr where the expression evaluated is a job output read.
234212
* eg: `${{ needs.job1.outputs.foo}}`
235213
*/
236214
class JobOutputAccessExpr extends ExprAccessExpr {
@@ -250,7 +228,7 @@ class JobOutputAccessExpr extends ExprAccessExpr {
250228
exists(JobStmt job |
251229
job.getId() = jobId and
252230
job.getLocation().getFile() = this.getLocation().getFile() and
253-
job.getJobOutputExpr(varName) = result
231+
job.getOutputStmt().getOutputExpr(varName) = result
254232
)
255233
}
256234
}

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

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -139,31 +139,40 @@ private import CfgImpl
139139
private import Completion
140140
private import CfgScope
141141

142-
// Trees are what end up creating Cfg::Node objects and therefore DataFlow::Node objects.
143-
// Its also required that there is parent/child relationships between nodes so orphans nodes will not appear as either Cfg::Node or DataFlow::Node.
144-
// For example
145-
// - ArgumentExpr should be children of UsesExpr, and UsesExpr should be children of StepStmt.
146-
// TODO: We need to make VarAccess expressions part ot the tree as they are currently orphans
147-
private class CfgNodeTree extends StandardPreOrderTree instanceof AstNode {
148-
override AstNode getChildNode(int i) { result = super.getChildNodeByOrder(i) }
142+
private class JobTree extends StandardPreOrderTree instanceof JobStmt {
143+
override ControlFlowTree getChildNode(int i) {
144+
result =
145+
rank[i](Expression child, Location l |
146+
(child = super.getAStep() or child = super.getOutputStmt()) and
147+
l = child.getLocation()
148+
|
149+
child
150+
order by
151+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
152+
)
153+
}
154+
}
155+
156+
private class JobOutputTree extends StandardPreOrderTree instanceof JobOutputStmt {
157+
override ControlFlowTree getChildNode(int i) { result = super.asYamlMapping().getValueNode(i) }
158+
}
159+
160+
private class UsesTree extends StandardPreOrderTree instanceof UsesExpr {
161+
override ControlFlowTree getChildNode(int i) {
162+
result =
163+
rank[i](Expression child, Location l |
164+
child = super.getArgument(_) and l = child.getLocation()
165+
|
166+
child
167+
order by
168+
l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine(), child.toString()
169+
)
170+
}
171+
}
172+
173+
private class RunTree extends StandardPreOrderTree instanceof RunExpr {
174+
override ControlFlowTree getChildNode(int i) { result = super.getScriptExpr() and i = 0 }
149175
}
150-
// private class JobStmtTree extends StandardPreOrderTree instanceof JobStmt {
151-
// override ControlFlowTree getChildNode(int i) { result = super.getSuccNode(i) }
152-
// }
153-
//
154-
// private class StepStmtTree extends StandardPreOrderTree instanceof StepStmt {
155-
// override ControlFlowTree getChildNode(int i) { result = super.getSuccNode(i) }
156-
// }
157-
//
158-
// private class JobOutputTree extends StandardPreOrderTree instanceof JobOutputStmt {
159-
// override ControlFlowTree getChildNode(int i) { result = super.getSuccNode(i) }
160-
// }
161-
//
162-
// // TODO: Do we need this or we can just care about the ExprAccessExpr
163-
// private class ArgumentTree extends LeafTree instanceof ArgumentExpr { }
164-
//
165-
// private class ExprAccessTree extends LeafTree instanceof ExprAccessExpr { }
166-
//
167-
// private class StepOutputAccessTree extends LeafTree instanceof StepOutputAccessExpr { }
168-
//
169-
// private class JobOutputAccessTree extends LeafTree instanceof JobOutputAccessExpr { }
176+
177+
private class ExprAccessTree extends LeafTree instanceof ExprAccessExpr { }
178+

0 commit comments

Comments
 (0)