@@ -299,40 +299,39 @@ private class NSStringRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
299299 * Regex("(a|b).*").firstMatch(in: myString)
300300 * ```
301301 */
302- class RegexEval extends CallExpr instanceof PotentialRegexEval {
303- RegexEval ( ) { this .doesEvaluate ( ) }
304-
302+ abstract class RegexEval extends CallExpr {
305303 /**
306304 * Gets the input to this call that is the regular expression being evaluated.
307305 * This may be a regular expression object or a string literal.
308306 *
309307 * Consider using `getARegex()` instead (which tracks the regular expression
310308 * input back to its source).
311309 */
312- Expr getRegexInput ( ) { result = super . getRegexInput ( ) . asExpr ( ) }
310+ abstract Expr getRegexInput ( ) ;
313311
314312 /**
315313 * Gets the input to this call that is the string the regular expression is evaluated on.
316314 */
317- Expr getStringInput ( ) { result = super . getStringInput ( ) . asExpr ( ) }
315+ abstract Expr getStringInput ( ) ;
318316
319317 /**
320318 * Gets a dataflow node for an options input that might contain parse mode
321319 * flags (if any).
322320 */
323- DataFlow:: Node getAnOptionsInput ( ) { result = super . getAnOptionsInput ( ) }
321+ DataFlow:: Node getAnOptionsInput ( ) { none ( ) }
324322
325323 /**
326324 * Gets a regular expression value that is evaluated here (if any can be identified).
327325 */
328326 RegExp getARegex ( ) {
329327 // 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 ( ) )
331330 or
332331 // string literal -> regex object -> use
333332 exists ( RegexCreation regexCreation |
334333 DataFlow:: exprNode ( result ) .( ParsedStringRegex ) .getAParse ( ) = regexCreation .getStringInput ( ) and
335- RegexUseFlow:: flow ( regexCreation , super .getRegexInput ( ) )
334+ RegexUseFlow:: flow ( regexCreation , DataFlow :: exprNode ( this .getRegexInput ( ) ) )
336335 )
337336 }
338337
@@ -346,50 +345,17 @@ class RegexEval extends CallExpr instanceof PotentialRegexEval {
346345 any ( RegexAdditionalFlowStep s ) .setsParseMode ( setNode , result , true ) and
347346 // reaches this eval
348347 (
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 ( ) )
351350 )
352351 )
353352 }
354353}
355354
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-
389355/**
390356 * A call to a function that always evaluates a regular expression.
391357 */
392- private class AlwaysRegexEval extends PotentialRegexEval {
358+ private class AlwaysRegexEval extends RegexEval {
393359 DataFlow:: Node regexInput ;
394360 DataFlow:: Node stringInput ;
395361
@@ -438,16 +404,18 @@ private class AlwaysRegexEval extends PotentialRegexEval {
438404 stringInput .asExpr ( ) = this .getQualifier ( )
439405 }
440406
441- override DataFlow :: Node getRegexInput ( ) { result = regexInput }
407+ override Expr getRegexInput ( ) { result = regexInput . asExpr ( ) }
442408
443- override DataFlow :: Node getStringInput ( ) { result = stringInput }
409+ override Expr getStringInput ( ) { result = stringInput . asExpr ( ) }
444410}
445411
446412/**
447413 * A call to a function that sometimes evaluates a regular expression, if
448414 * `NSString.CompareOptions.regularExpression` is set as an `options` argument.
415+ *
416+ * This is a helper class for `NSStringCompareOptionsRegexEval`.
449417 */
450- private class NSStringCompareOptionsPotentialRegexEval extends PotentialRegexEval {
418+ private class NSStringCompareOptionsPotentialRegexEval extends CallExpr {
451419 DataFlow:: Node regexInput ;
452420 DataFlow:: Node stringInput ;
453421 DataFlow:: Node optionsInput ;
@@ -472,15 +440,58 @@ private class NSStringCompareOptionsPotentialRegexEval extends PotentialRegexEva
472440 optionsInput .asExpr ( ) = this .getArgumentWithLabel ( "options" ) .getExpr ( )
473441 }
474442
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+ }
476449
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+ }
478463
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+ }
480468
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
482487 // check there is flow from a `NSString.CompareOptions.regularExpression` value to an `options` argument;
483488 // if there isn't, the input won't be interpretted as a regular expression.
484- RegexEnableFlagFlow :: flow ( _, optionsInput )
489+ NSStringCompareOptionsFlagFlow :: flow ( _, potentialEval . getAnOptionsInput ( ) )
485490 }
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 ( ) }
486497}
0 commit comments