@@ -133,6 +133,7 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
133
133
134
134
newtype TContent =
135
135
TFieldContent ( string name ) {
136
+ // We only use field flow for steps and jobs outputs, not for accessing other context fields such as env or inputs
136
137
name = any ( StepsCtxAccessExpr a ) .getFieldName ( ) or
137
138
name = any ( NeedsCtxAccessExpr a ) .getFieldName ( ) or
138
139
name = any ( JobsCtxAccessExpr a ) .getFieldName ( )
@@ -188,35 +189,22 @@ class ArgumentPosition extends string {
188
189
predicate parameterMatch ( ParameterPosition ppos , ArgumentPosition apos ) { ppos = apos }
189
190
190
191
/**
191
- * Holds if there is a local flow step between a ${{}} expression accesing a step output variable and the step output itself
192
- * But only for those cases where the step output is defined externally in a MaD specification.
193
- * The reason for this is that we don't currently have a way to specify that a source starts with a non-empty access
194
- * path so the easiest thing is to add the corresponding read steps of that field as local flow steps as well.
195
- * e.g. ${{ steps.step1.output.foo }}
192
+ * Holds if there is a local flow step between a ${{ steps.xxx.outputs.yyy }} expression accesing a step output field
193
+ * and the step output itself. But only for those cases where the step output is defined externally in a MaD Source
194
+ * specification. The reason for this is that we don't currently have a way to specify that a source starts with a
195
+ * non-empty access path so we cannot write a Source that stores the taint in a Content, we can only do that for steps
196
+ * (storeStep). The easiest thing is to add this local flow step that simulates a read step from the source node for a specific
197
+ * field name.
196
198
*/
197
199
predicate stepsCtxLocalStep ( Node nodeFrom , Node nodeTo ) {
198
- exists ( StepStmt astFrom , StepsCtxAccessExpr astTo |
200
+ exists ( UsesExpr astFrom , StepsCtxAccessExpr astTo |
199
201
externallyDefinedSource ( nodeFrom , _, "output." + astTo .getFieldName ( ) ) and
200
- astFrom instanceof UsesExpr and
201
202
astFrom = nodeFrom .asExpr ( ) and
202
203
astTo = nodeTo .asExpr ( ) and
203
204
astTo .getRefExpr ( ) = astFrom
204
205
)
205
206
}
206
207
207
- /**
208
- * Holds if there is a local flow step between a ${{}} expression accesing a job output variable and the job output itself
209
- * e.g. ${{ needs.job1.output.foo }} or ${{ jobs.job1.output.foo }}
210
- */
211
- predicate jobsCtxLocalStep ( Node nodeFrom , Node nodeTo ) {
212
- exists ( Expression astFrom , CtxAccessExpr astTo |
213
- astFrom = nodeFrom .asExpr ( ) and
214
- astTo = nodeTo .asExpr ( ) and
215
- astTo .getRefExpr ( ) = astFrom and
216
- ( astTo instanceof NeedsCtxAccessExpr or astTo instanceof JobsCtxAccessExpr )
217
- )
218
- }
219
-
220
208
/**
221
209
* Holds if there is a local flow step between a ${{}} expression accesing an input variable and the input itself
222
210
* e.g. ${{ inputs.foo }}
@@ -252,7 +240,6 @@ predicate envCtxLocalStep(Node nodeFrom, Node nodeTo) {
252
240
pragma [ nomagic]
253
241
predicate localFlowStep ( Node nodeFrom , Node nodeTo ) {
254
242
stepsCtxLocalStep ( nodeFrom , nodeTo ) or
255
- jobsCtxLocalStep ( nodeFrom , nodeTo ) or
256
243
inputsCtxLocalStep ( nodeFrom , nodeTo ) or
257
244
envCtxLocalStep ( nodeFrom , nodeTo )
258
245
}
@@ -273,42 +260,35 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { localFlowStep(nodeFr
273
260
predicate jumpStep ( Node nodeFrom , Node nodeTo ) { none ( ) }
274
261
275
262
/**
276
- * A read step to read the value of a ReusableWork uses step and connect it to its
277
- * corresponding JobOutputAccessExpr
263
+ * Holds if a CtxAccessExpr reads a field from a job (needs/jobs), step (steps) output via a read of `c` (fieldname)
278
264
*/
279
- predicate reusableWorkflowReturnReadStep ( Node node1 , Node node2 , ContentSet c ) {
280
- exists ( NeedsCtxAccessExpr expr , string fieldName |
281
- expr .usesReusableWorkflow ( ) and
282
- expr .getRefExpr ( ) = node1 .asExpr ( ) and
283
- expr .getFieldName ( ) = fieldName and
284
- expr = node2 .asExpr ( ) and
285
- c = any ( FieldContent ct | ct .getName ( ) = fieldName )
265
+ predicate ctxFieldReadStep ( Node node1 , Node node2 , ContentSet c ) {
266
+ exists ( CtxAccessExpr access |
267
+ (
268
+ access instanceof NeedsCtxAccessExpr or
269
+ access instanceof StepsCtxAccessExpr or
270
+ access instanceof JobsCtxAccessExpr
271
+ ) and
272
+ c = any ( FieldContent ct | ct .getName ( ) = access .getFieldName ( ) ) and
273
+ node1 .asExpr ( ) = access .getRefExpr ( ) and
274
+ node2 .asExpr ( ) = access
286
275
)
287
276
}
288
277
289
278
/**
290
279
* Holds if data can flow from `node1` to `node2` via a read of `c`. Thus,
291
280
* `node1` references an object with a content `c.getAReadContent()` whose
292
281
* value ends up in `node2`.
282
+ * Store steps without corresponding reads are pruned aggressively very early, since they can never contribute to a complete path.
293
283
*/
294
- predicate readStep ( Node node1 , ContentSet c , Node node2 ) {
295
- // TODO: Extract to its own predicate
296
- exists ( StepsCtxAccessExpr access |
297
- c = any ( FieldContent ct | ct .getName ( ) = access .getFieldName ( ) ) and
298
- node1 .asExpr ( ) = access .getRefExpr ( ) and
299
- node2 .asExpr ( ) = access
300
- )
301
- or
302
- reusableWorkflowReturnReadStep ( node1 , node2 , c )
303
- }
284
+ predicate readStep ( Node node1 , ContentSet c , Node node2 ) { ctxFieldReadStep ( node1 , node2 , c ) }
304
285
305
286
/**
306
- * A store step to store the value of a ReusableWorkflowStmt output expr into the return node (node2)
307
- * with a given access path (fieldName)
287
+ * Stores an output expression (node1) into its OutputsStm node (node2)
288
+ * using the output variable name as the access path
308
289
*/
309
- predicate reusableWorkflowReturnStoreStep ( Node node1 , Node node2 , ContentSet c ) {
310
- exists ( ReusableWorkflowStmt stmt , OutputsStmt out , string fieldName |
311
- out = stmt .getOutputsStmt ( ) and
290
+ predicate fieldStoreStep ( Node node1 , Node node2 , ContentSet c ) {
291
+ exists ( OutputsStmt out , string fieldName |
312
292
node1 .asExpr ( ) = out .getOutputExpr ( fieldName ) and
313
293
node2 .asExpr ( ) = out and
314
294
c = any ( FieldContent ct | ct .getName ( ) = fieldName )
@@ -319,15 +299,12 @@ predicate reusableWorkflowReturnStoreStep(Node node1, Node node2, ContentSet c)
319
299
* Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
320
300
* `node2` references an object with a content `c.getAStoreContent()` that
321
301
* contains the value of `node1`.
302
+ * Store steps without corresponding reads are pruned aggressively very early, since they can never contribute to a complete path.
322
303
*/
323
304
predicate storeStep ( Node node1 , ContentSet c , Node node2 ) {
324
- reusableWorkflowReturnStoreStep ( node1 , node2 , c )
325
- or
326
- // TODO: rename to xxxxStoreStep
327
- externallyDefinedSummary ( node1 , node2 , c )
328
- or
329
- // TODO: rename to xxxxStoreStep
330
- runEnvToScriptstep ( node1 , node2 , c )
305
+ fieldStoreStep ( node1 , node2 , c ) or
306
+ externallyDefinedStoreStep ( node1 , node2 , c ) or
307
+ runEnvToScriptStoreStep ( node1 , node2 , c )
331
308
}
332
309
333
310
/**
0 commit comments