@@ -299,40 +299,39 @@ private class NSStringRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
299
299
* Regex("(a|b).*").firstMatch(in: myString)
300
300
* ```
301
301
*/
302
- class RegexEval extends CallExpr instanceof PotentialRegexEval {
303
- RegexEval ( ) { this .doesEvaluate ( ) }
304
-
302
+ abstract class RegexEval extends CallExpr {
305
303
/**
306
304
* Gets the input to this call that is the regular expression being evaluated.
307
305
* This may be a regular expression object or a string literal.
308
306
*
309
307
* Consider using `getARegex()` instead (which tracks the regular expression
310
308
* input back to its source).
311
309
*/
312
- Expr getRegexInput ( ) { result = super . getRegexInput ( ) . asExpr ( ) }
310
+ abstract Expr getRegexInput ( ) ;
313
311
314
312
/**
315
313
* Gets the input to this call that is the string the regular expression is evaluated on.
316
314
*/
317
- Expr getStringInput ( ) { result = super . getStringInput ( ) . asExpr ( ) }
315
+ abstract Expr getStringInput ( ) ;
318
316
319
317
/**
320
318
* Gets a dataflow node for an options input that might contain parse mode
321
319
* flags (if any).
322
320
*/
323
- DataFlow:: Node getAnOptionsInput ( ) { result = super . getAnOptionsInput ( ) }
321
+ DataFlow:: Node getAnOptionsInput ( ) { none ( ) }
324
322
325
323
/**
326
324
* Gets a regular expression value that is evaluated here (if any can be identified).
327
325
*/
328
326
RegExp getARegex ( ) {
329
327
// string literal used directly as a regex
330
- DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = super .getRegexInput ( )
328
+ DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) =
329
+ DataFlow:: exprNode ( this .getRegexInput ( ) )
331
330
or
332
331
// string literal -> regex object -> use
333
332
exists ( RegexCreation regexCreation |
334
333
DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = regexCreation .getStringInput ( ) and
335
- RegexUseFlow:: flow ( regexCreation , super .getRegexInput ( ) )
334
+ RegexUseFlow:: flow ( regexCreation , DataFlow :: exprNode ( this .getRegexInput ( ) ) )
336
335
)
337
336
}
338
337
@@ -346,50 +345,17 @@ class RegexEval extends CallExpr instanceof PotentialRegexEval {
346
345
any ( RegexAdditionalFlowStep s ) .setsParseMode ( setNode , result , true ) and
347
346
// reaches this eval
348
347
(
349
- RegexParseModeFlow:: flow ( setNode , super .getRegexInput ( ) ) or
350
- RegexParseModeFlow:: flow ( setNode , super .getAnOptionsInput ( ) )
348
+ RegexParseModeFlow:: flow ( setNode , DataFlow :: exprNode ( this .getRegexInput ( ) ) ) or
349
+ RegexParseModeFlow:: flow ( setNode , this .getAnOptionsInput ( ) )
351
350
)
352
351
)
353
352
}
354
353
}
355
354
356
- /**
357
- * A call that may evaluate a regular expression. Extend this abstract class to
358
- * add new regular expression evaluation models.
359
- */
360
- abstract class PotentialRegexEval extends CallExpr {
361
- /**
362
- * Gets the input to this call that is the regular expression being evaluated.
363
- * This may be a regular expression object or a string literal.
364
- */
365
- abstract DataFlow:: Node getRegexInput ( ) ;
366
-
367
- /**
368
- * Gets the input to this call that is the string the regular expression is evaluated on.
369
- */
370
- abstract DataFlow:: Node getStringInput ( ) ;
371
-
372
- /**
373
- * Gets a dataflow node for an options input that might contain parse mode
374
- * flags (if any).
375
- */
376
- DataFlow:: Node getAnOptionsInput ( ) { none ( ) }
377
-
378
- /**
379
- * Holds if this instance actually evaluates a regular expression. If this
380
- * does not hold, a `RegexEval` is not created for this `PotentialRegexEval`.
381
- *
382
- * This mechanism exists so that we have something to track flow of options
383
- * into (for example an `NSString.CompareOptions.regularExpression` option)
384
- * before deciding whether a regular expression is actually evaluated.
385
- */
386
- predicate doesEvaluate ( ) { any ( ) }
387
- }
388
-
389
355
/**
390
356
* A call to a function that always evaluates a regular expression.
391
357
*/
392
- private class AlwaysRegexEval extends PotentialRegexEval {
358
+ private class AlwaysRegexEval extends RegexEval {
393
359
DataFlow:: Node regexInput ;
394
360
DataFlow:: Node stringInput ;
395
361
@@ -438,16 +404,18 @@ private class AlwaysRegexEval extends PotentialRegexEval {
438
404
stringInput .asExpr ( ) = this .getQualifier ( )
439
405
}
440
406
441
- override DataFlow :: Node getRegexInput ( ) { result = regexInput }
407
+ override Expr getRegexInput ( ) { result = regexInput . asExpr ( ) }
442
408
443
- override DataFlow :: Node getStringInput ( ) { result = stringInput }
409
+ override Expr getStringInput ( ) { result = stringInput . asExpr ( ) }
444
410
}
445
411
446
412
/**
447
413
* A call to a function that sometimes evaluates a regular expression, if
448
414
* `NSString.CompareOptions.regularExpression` is set as an `options` argument.
415
+ *
416
+ * This is a helper class for `NSStringCompareOptionsRegexEval`.
449
417
*/
450
- private class NSStringCompareOptionsPotentialRegexEval extends PotentialRegexEval {
418
+ private class NSStringCompareOptionsPotentialRegexEval extends CallExpr {
451
419
DataFlow:: Node regexInput ;
452
420
DataFlow:: Node stringInput ;
453
421
DataFlow:: Node optionsInput ;
@@ -472,15 +440,58 @@ private class NSStringCompareOptionsPotentialRegexEval extends PotentialRegexEva
472
440
optionsInput .asExpr ( ) = this .getArgumentWithLabel ( "options" ) .getExpr ( )
473
441
}
474
442
475
- override DataFlow:: Node getRegexInput ( ) { result = regexInput }
443
+ DataFlow:: Node getRegexInput ( ) { result = regexInput }
444
+
445
+ DataFlow:: Node getStringInput ( ) { result = stringInput }
446
+
447
+ DataFlow:: Node getAnOptionsInput ( ) { result = optionsInput }
448
+ }
476
449
477
- override DataFlow:: Node getStringInput ( ) { result = stringInput }
450
+ /**
451
+ * A data flow configuration for tracking `NSString.CompareOptions.regularExpression`
452
+ * values from where they are created to the point of use.
453
+ */
454
+ private module NSStringCompareOptionsFlagConfig implements DataFlow:: ConfigSig {
455
+ predicate isSource ( DataFlow:: Node node ) {
456
+ // creation of a `NSString.CompareOptions.regularExpression` value
457
+ node .asExpr ( )
458
+ .( MemberRefExpr )
459
+ .getMember ( )
460
+ .( FieldDecl )
461
+ .hasQualifiedName ( "NSString.CompareOptions" , "regularExpression" )
462
+ }
478
463
479
- override DataFlow:: Node getAnOptionsInput ( ) { result = optionsInput }
464
+ predicate isSink ( DataFlow:: Node node ) {
465
+ // use in a regex eval `options` argument
466
+ any ( NSStringCompareOptionsPotentialRegexEval potentialEval ) .getAnOptionsInput ( ) = node
467
+ }
480
468
481
- override predicate doesEvaluate ( ) {
469
+ predicate allowImplicitRead ( DataFlow:: Node node , DataFlow:: ContentSet c ) {
470
+ // flow out from collection content at the sink.
471
+ isSink ( node ) and
472
+ c .getAReadContent ( ) instanceof DataFlow:: Content:: CollectionContent
473
+ }
474
+ }
475
+
476
+ module NSStringCompareOptionsFlagFlow = DataFlow:: Global< NSStringCompareOptionsFlagConfig > ;
477
+
478
+ /**
479
+ * A call to a function that evaluates a regular expression because
480
+ * `NSString.CompareOptions.regularExpression` is set as an `options` argument.
481
+ */
482
+ private class NSStringCompareOptionsRegexEval extends RegexEval {
483
+ NSStringCompareOptionsPotentialRegexEval potentialEval ;
484
+
485
+ NSStringCompareOptionsRegexEval ( ) {
486
+ this = potentialEval and
482
487
// check there is flow from a `NSString.CompareOptions.regularExpression` value to an `options` argument;
483
488
// if there isn't, the input won't be interpretted as a regular expression.
484
- RegexEnableFlagFlow :: flow ( _, optionsInput )
489
+ NSStringCompareOptionsFlagFlow :: flow ( _, potentialEval . getAnOptionsInput ( ) )
485
490
}
491
+
492
+ override Expr getRegexInput ( ) { result = potentialEval .getRegexInput ( ) .asExpr ( ) }
493
+
494
+ override Expr getStringInput ( ) { result = potentialEval .getStringInput ( ) .asExpr ( ) }
495
+
496
+ override DataFlow:: Node getAnOptionsInput ( ) { result = potentialEval .getAnOptionsInput ( ) }
486
497
}
0 commit comments