11private import codeql.actions.ast.internal.Actions
22private 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+ */
412class 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+ */
105117class 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 {
116136class 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 */
200178class 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 */
214192class 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 */
236214class 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}
0 commit comments