@@ -299,35 +299,21 @@ class NSStringRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
299
299
* Regex("(a|b).*").firstMatch(in: myString)
300
300
* ```
301
301
*/
302
- abstract class RegexEval extends CallExpr {
303
- /**
304
- * Gets the input to this call that is the regular expression being evaluated.
305
- * This may be a regular expression object or a string literal.
306
- */
307
- abstract DataFlow:: Node getRegexInput ( ) ;
308
-
309
- /**
310
- * Gets the input to this call that is the string the regular expression is evaluated on.
311
- */
312
- abstract DataFlow:: Node getStringInput ( ) ;
313
-
314
- /**
315
- * Gets a dataflow node for the options input that might contain parse mode
316
- * flags (if any).
317
- */
318
- DataFlow:: Node getAnOptionsInput ( ) { none ( ) }
302
+ class RegexEval extends CallExpr instanceof PotentialRegexEval {
303
+ RegexEval ( ) { this .( PotentialRegexEval ) .doesEvaluate ( ) }
319
304
320
305
/**
321
306
* Gets a regular expression value that is evaluated here (if any can be identified).
322
307
*/
323
308
RegExp getARegex ( ) {
324
309
// string literal used directly as a regex
325
- DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = this .getRegexInput ( )
310
+ DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) =
311
+ this .( PotentialRegexEval ) .getRegexInput ( )
326
312
or
327
313
// string literal -> regex object -> use
328
314
exists ( RegexCreation regexCreation |
329
315
DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = regexCreation .getStringInput ( ) and
330
- RegexUseFlow:: flow ( regexCreation , this .getRegexInput ( ) )
316
+ RegexUseFlow:: flow ( regexCreation , this .( PotentialRegexEval ) . getRegexInput ( ) )
331
317
)
332
318
}
333
319
@@ -341,17 +327,50 @@ abstract class RegexEval extends CallExpr {
341
327
any ( RegexAdditionalFlowStep s ) .setsParseMode ( setNode , result , true ) and
342
328
// reaches this eval
343
329
(
344
- RegexParseModeFlow:: flow ( setNode , this .getRegexInput ( ) ) or
345
- RegexParseModeFlow:: flow ( setNode , this .getAnOptionsInput ( ) )
330
+ RegexParseModeFlow:: flow ( setNode , this .( PotentialRegexEval ) . getRegexInput ( ) ) or
331
+ RegexParseModeFlow:: flow ( setNode , this .( PotentialRegexEval ) . getAnOptionsInput ( ) )
346
332
)
347
333
)
348
334
}
349
335
}
350
336
337
+ /**
338
+ * A call that may evaluate a regular expression. Extend this abstract class to
339
+ * add new regular expression evaluation models.
340
+ */
341
+ abstract class PotentialRegexEval extends CallExpr {
342
+ /**
343
+ * Gets the input to this call that is the regular expression being evaluated.
344
+ * This may be a regular expression object or a string literal.
345
+ */
346
+ abstract DataFlow:: Node getRegexInput ( ) ;
347
+
348
+ /**
349
+ * Gets the input to this call that is the string the regular expression is evaluated on.
350
+ */
351
+ abstract DataFlow:: Node getStringInput ( ) ;
352
+
353
+ /**
354
+ * Gets a dataflow node for the options input that might contain parse mode
355
+ * flags (if any).
356
+ */
357
+ DataFlow:: Node getAnOptionsInput ( ) { none ( ) }
358
+
359
+ /**
360
+ * Holds if this is an actual regular expression evalaution. If this does not
361
+ * hold, the potential regular expression evaluation should be discarded.
362
+ *
363
+ * This mechanism exists so that we have something to track flow of options
364
+ * into to (for example an `NSString.CompareOptions.regularExpression` option)
365
+ * before deciding whether a regular expression is actually evaluated.
366
+ */
367
+ predicate doesEvaluate ( ) { any ( ) }
368
+ }
369
+
351
370
/**
352
371
* A call to a function that always evaluates a regular expression.
353
372
*/
354
- private class AlwaysRegexEval extends RegexEval {
373
+ private class AlwaysRegexEval extends PotentialRegexEval {
355
374
DataFlow:: Node regexInput ;
356
375
DataFlow:: Node stringInput ;
357
376
@@ -409,7 +428,7 @@ private class AlwaysRegexEval extends RegexEval {
409
428
* A call to a function that sometimes evaluates a regular expression, if
410
429
* `NSString.CompareOptions.regularExpression` is set as an `options` argument.
411
430
*/
412
- private class NSStringCompareOptionsMaybeRegexEval extends RegexEval {
431
+ private class NSStringCompareOptionsMaybeRegexEval extends PotentialRegexEval {
413
432
DataFlow:: Node regexInput ;
414
433
DataFlow:: Node stringInput ;
415
434
DataFlow:: Node optionsInput ;
@@ -434,20 +453,21 @@ private class NSStringCompareOptionsMaybeRegexEval extends RegexEval {
434
453
optionsInput .asExpr ( ) = this .getArgumentWithLabel ( "options" ) .getExpr ( )
435
454
}
436
455
437
- override DataFlow:: Node getRegexInput ( ) {
456
+ override DataFlow:: Node getRegexInput ( ) { result = regexInput }
457
+
458
+ override DataFlow:: Node getStringInput ( ) { result = stringInput }
459
+
460
+ override DataFlow:: Node getAnOptionsInput ( ) { result = optionsInput }
461
+
462
+ override predicate doesEvaluate ( ) {
438
463
// check there is flow from a `NSString.CompareOptions.regularExpression` value to an `options` argument;
439
- // if it isn't, the input won't be interpretted as a regular expression and we should discard it .
464
+ // if it isn't, the input won't be interpretted as a regular expression.
440
465
exists ( MemberRefExpr sourceValue |
441
466
sourceValue
442
467
.getMember ( )
443
468
.( FieldDecl )
444
469
.hasQualifiedName ( "NSString.CompareOptions" , "regularExpression" ) and
445
470
RegexEnableFlagFlow:: flow ( DataFlow:: exprNode ( sourceValue ) , optionsInput )
446
- ) and
447
- result = regexInput
471
+ )
448
472
}
449
-
450
- override DataFlow:: Node getStringInput ( ) { result = stringInput }
451
-
452
- override DataFlow:: Node getAnOptionsInput ( ) { result = optionsInput }
453
473
}
0 commit comments