@@ -299,35 +299,21 @@ class NSStringRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
299299 * Regex("(a|b).*").firstMatch(in: myString)
300300 * ```
301301 */
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 ( ) }
319304
320305 /**
321306 * Gets a regular expression value that is evaluated here (if any can be identified).
322307 */
323308 RegExp getARegex ( ) {
324309 // 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 ( )
326312 or
327313 // string literal -> regex object -> use
328314 exists ( RegexCreation regexCreation |
329315 DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = regexCreation .getStringInput ( ) and
330- RegexUseFlow:: flow ( regexCreation , this .getRegexInput ( ) )
316+ RegexUseFlow:: flow ( regexCreation , this .( PotentialRegexEval ) . getRegexInput ( ) )
331317 )
332318 }
333319
@@ -341,17 +327,50 @@ abstract class RegexEval extends CallExpr {
341327 any ( RegexAdditionalFlowStep s ) .setsParseMode ( setNode , result , true ) and
342328 // reaches this eval
343329 (
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 ( ) )
346332 )
347333 )
348334 }
349335}
350336
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+
351370/**
352371 * A call to a function that always evaluates a regular expression.
353372 */
354- private class AlwaysRegexEval extends RegexEval {
373+ private class AlwaysRegexEval extends PotentialRegexEval {
355374 DataFlow:: Node regexInput ;
356375 DataFlow:: Node stringInput ;
357376
@@ -409,7 +428,7 @@ private class AlwaysRegexEval extends RegexEval {
409428 * A call to a function that sometimes evaluates a regular expression, if
410429 * `NSString.CompareOptions.regularExpression` is set as an `options` argument.
411430 */
412- private class NSStringCompareOptionsMaybeRegexEval extends RegexEval {
431+ private class NSStringCompareOptionsMaybeRegexEval extends PotentialRegexEval {
413432 DataFlow:: Node regexInput ;
414433 DataFlow:: Node stringInput ;
415434 DataFlow:: Node optionsInput ;
@@ -434,20 +453,21 @@ private class NSStringCompareOptionsMaybeRegexEval extends RegexEval {
434453 optionsInput .asExpr ( ) = this .getArgumentWithLabel ( "options" ) .getExpr ( )
435454 }
436455
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 ( ) {
438463 // 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.
440465 exists ( MemberRefExpr sourceValue |
441466 sourceValue
442467 .getMember ( )
443468 .( FieldDecl )
444469 .hasQualifiedName ( "NSString.CompareOptions" , "regularExpression" ) and
445470 RegexEnableFlagFlow:: flow ( DataFlow:: exprNode ( sourceValue ) , optionsInput )
446- ) and
447- result = regexInput
471+ )
448472 }
449-
450- override DataFlow:: Node getStringInput ( ) { result = stringInput }
451-
452- override DataFlow:: Node getAnOptionsInput ( ) { result = optionsInput }
453473}
0 commit comments