@@ -81,6 +81,8 @@ class WorkflowStmt extends Statement instanceof Actions::Workflow {
8181 }
8282
8383 Statement getPermissionsStmt ( ) { result = this .( YamlMapping ) .lookup ( "permissions" ) }
84+
85+ StrategyStmt getStrategyStmt ( ) { result = this .( YamlMapping ) .lookup ( "strategy" ) }
8486}
8587
8688class ReusableWorkflowStmt extends WorkflowStmt {
@@ -125,6 +127,23 @@ class OutputsStmt extends Statement instanceof YamlMapping {
125127 string getAnOutputName ( ) { this .( YamlMapping ) .maps ( any ( YamlString s | s .getValue ( ) = result ) , _) }
126128}
127129
130+ class StrategyStmt extends Statement instanceof YamlMapping {
131+ YamlMapping parent ;
132+
133+ StrategyStmt ( ) { parent .lookup ( "strategy" ) = this }
134+
135+ /**
136+ * Gets a specific matric expression (YamlMapping) by name.
137+ */
138+ MatrixVariableExpr getMatrixVariableExpr ( string name ) {
139+ this .( YamlMapping ) .lookup ( "matrix" ) .( YamlMapping ) .lookup ( name ) = result
140+ }
141+
142+ string getAMatrixVariableName ( ) {
143+ this .( YamlMapping ) .maps ( any ( YamlString s | s .getValue ( ) = result ) , _)
144+ }
145+ }
146+
128147class InputExpr extends Expression instanceof YamlString {
129148 InputExpr ( ) { exists ( InputsStmt inputs | inputs .( YamlMapping ) .maps ( this , _) ) }
130149}
@@ -138,6 +157,14 @@ class OutputExpr extends Expression instanceof YamlString {
138157 }
139158}
140159
160+ class MatrixVariableExpr extends Expression instanceof YamlString {
161+ MatrixVariableExpr ( ) {
162+ exists ( StrategyStmt outputs |
163+ outputs .( YamlMapping ) .lookup ( "matrix" ) .( YamlMapping ) .lookup ( _) = this
164+ )
165+ }
166+ }
167+
141168/**
142169 * A Job is a collection of steps that run in an execution environment.
143170 */
@@ -191,6 +218,8 @@ class JobStmt extends Statement instanceof Actions::Job {
191218 IfStmt getIfStmt ( ) { result = super .getIf ( ) }
192219
193220 Statement getPermissionsStmt ( ) { result = this .( YamlMapping ) .lookup ( "permissions" ) }
221+
222+ StrategyStmt getStrategyStmt ( ) { result = this .( YamlMapping ) .lookup ( "strategy" ) }
194223}
195224
196225/**
@@ -332,7 +361,8 @@ class ExprAccessExpr extends Expression instanceof YamlString {
332361class CtxAccessExpr extends ExprAccessExpr {
333362 CtxAccessExpr ( ) {
334363 expr .regexpMatch ( [
335- stepsCtxRegex ( ) , needsCtxRegex ( ) , jobsCtxRegex ( ) , envCtxRegex ( ) , inputsCtxRegex ( )
364+ stepsCtxRegex ( ) , needsCtxRegex ( ) , jobsCtxRegex ( ) , envCtxRegex ( ) , inputsCtxRegex ( ) ,
365+ matrixCtxRegex ( )
336366 ] )
337367 }
338368
@@ -342,22 +372,28 @@ class CtxAccessExpr extends ExprAccessExpr {
342372}
343373
344374private string stepsCtxRegex ( ) {
345- result = "\\bsteps\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)\\b"
375+ result = wrapRegexp ( "steps\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)" )
346376}
347377
348378private string needsCtxRegex ( ) {
349- result = "\\bneeds\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)\\b"
379+ result = wrapRegexp ( "needs\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)" )
350380}
351381
352382private string jobsCtxRegex ( ) {
353- result = "\\bjobs\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)\\b"
383+ result = wrapRegexp ( "jobs\\ .([A-Za-z0-9_-]+)\\.outputs\\.([A-Za-z0-9_-]+)" )
354384}
355385
356- private string envCtxRegex ( ) { result = "\\benv\\.([A-Za-z0-9_-]+)\\b" }
386+ private string envCtxRegex ( ) { result = wrapRegexp ( "env\\.([A-Za-z0-9_-]+)" ) }
387+
388+ private string matrixCtxRegex ( ) { result = wrapRegexp ( "matrix\\.([A-Za-z0-9_-]+)" ) }
357389
358390private string inputsCtxRegex ( ) {
359- result = "\\binputs\\.([A-Za-z0-9_-]+)\\b" or
360- result = "\\bgithub\\.event\\.inputs\\.([A-Za-z0-9_-]+)\\b"
391+ result = wrapRegexp ( [ "inputs\\.([A-Za-z0-9_-]+)" , "github\\.event\\.inputs\\.([A-Za-z0-9_-]+)" ] )
392+ }
393+
394+ bindingset [ regex]
395+ private string wrapRegexp ( string regex ) {
396+ result = [ "\\b" + regex + "\\b" , "fromJSON\\(" + regex + "\\)" , "toJSON\\(" + regex + "\\)" ]
361397}
362398
363399/**
@@ -487,3 +523,31 @@ class EnvCtxAccessExpr extends CtxAccessExpr {
487523 )
488524 }
489525}
526+
527+ /**
528+ * Holds for an expression accesing the `matrix` context.
529+ * https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
530+ * e.g. `${{ matrix.foo }}`
531+ */
532+ class MatrixCtxAccessExpr extends CtxAccessExpr {
533+ string fieldName ;
534+
535+ MatrixCtxAccessExpr ( ) {
536+ expr .regexpMatch ( matrixCtxRegex ( ) ) and
537+ fieldName = expr .regexpCapture ( matrixCtxRegex ( ) , 1 )
538+ }
539+
540+ override string getFieldName ( ) { result = fieldName }
541+
542+ override Expression getRefExpr ( ) {
543+ exists ( WorkflowStmt w |
544+ w .getStrategyStmt ( ) .getMatrixVariableExpr ( fieldName ) = result and
545+ w .getAChildNode * ( ) = this
546+ )
547+ or
548+ exists ( JobStmt j |
549+ j .getStrategyStmt ( ) .getMatrixVariableExpr ( fieldName ) = result and
550+ j .getAChildNode * ( ) = this
551+ )
552+ }
553+ }
0 commit comments