44
55import swift
66import codeql.swift.dataflow.DataFlow
7-
87import codeql.swift.regex.RegexTreeView // re-export
98private import internal.ParseRegex
10- //private import codeql.regex.internal.RegExpTracking as RegExpTracking
119
1210/**
13- * A node whose value may flow to a position where it is interpreted
14- * as a part of a regular expression .
11+ * A data flow configuration for tracking string literals that are used as
12+ * regular expressions .
1513 */
16- abstract class RegExpPatternSource extends DataFlow:: Node {
17- /**
18- * Gets a node where the pattern of this node is parsed as a part of
19- * a regular expression.
20- */
21- abstract DataFlow:: Node getAParse ( ) ;
22-
23- /**
24- * Gets the root term of the regular expression parsed from this pattern.
25- */
26- abstract RegExpTerm getRegExpTerm ( ) ;
27- }
28-
29- /* *
30- * A node whose string value may flow to a position where it is interpreted
31- * as a part of a regular expression.
32- *
33- private class StringRegExpPatternSource extends RegExpPatternSource {
34- private DataFlow::Node parse;
35-
36- StringRegExpPatternSource() {
37- this = regExpSource(parse) and
38- // `regExpSource()` tracks both strings and regex literals, narrow it down to strings.
39- this.asExpr().getConstantValue().isString(_)
14+ private module RegexUseConfig implements DataFlow:: ConfigSig {
15+ predicate isSource ( DataFlow:: Node node ) { node .asExpr ( ) instanceof StringLiteralExpr }
16+
17+ predicate isSink ( DataFlow:: Node node ) { node .asExpr ( ) = any ( RegexEval eval ) .getRegexInput ( ) }
18+
19+ predicate isAdditionalFlowStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
20+ // flow through `Regex` initializer, i.e. from a string to a `Regex` object.
21+ exists ( CallExpr call |
22+ (
23+ call .getStaticTarget ( ) .( Method ) .hasQualifiedName ( "Regex" , [ "init(_:)" , "init(_:as:)" ] ) or
24+ call .getStaticTarget ( )
25+ .( Method )
26+ .hasQualifiedName ( "NSRegularExpression" , "init(pattern:options:)" )
27+ ) and
28+ nodeFrom .asExpr ( ) = call .getArgument ( 0 ) .getExpr ( ) and
29+ nodeTo .asExpr ( ) = call
30+ )
4031 }
32+ }
4133
42- override DataFlow::Node getAParse() { result = parse }
43-
44- override RegExpTerm getRegExpTerm() { result.getRegExp() = this.asExpr().getExpr() }
45- }*/
34+ private module RegexUseFlow = DataFlow:: Global< RegexUseConfig > ;
4635
4736/**
48- * TODO
49- * "(a|b).*"
37+ * A string literal that is used as a regular expression in a regular
38+ * expression evaluation. For example the string literal `"(a|b).*"` in:
39+ * ```
40+ * Regex("(a|b).*").firstMatch(in: myString)
41+ * ```
5042 */
51- private class ParsedStringRegExp extends RegExp , StringLiteralExpr {
52- private DataFlow :: Node parse ;
43+ private class ParsedStringRegex extends RegExp , StringLiteralExpr {
44+ RegexEval eval ;
5345
54- ParsedStringRegExp ( ) {
55- //this = regExpSource(parse).asExpr().getExpr()
56- parse .asExpr ( ) = this
46+ ParsedStringRegex ( ) {
47+ RegexUseFlow:: flow ( DataFlow:: exprNode ( this ) , DataFlow:: exprNode ( eval .getRegexInput ( ) ) )
5748 }
5849
59- DataFlow:: Node getAParse ( ) { result = parse }
60- /*
61- override predicate isDotAll() { none() }
62-
63- override predicate isIgnoreCase() { none() }
64-
65- override string getFlags() { none() }*/
50+ /**
51+ * Gets a call that evaluates this regular expression.
52+ */
53+ RegexEval getEval ( ) { result = eval }
6654}
6755
6856/**
@@ -72,18 +60,23 @@ private class ParsedStringRegExp extends RegExp, StringLiteralExpr {
7260 * ```
7361 */
7462abstract class RegexEval extends CallExpr {
75- Expr regex ;
76- Expr input ;
63+ Expr regexInput ;
64+ Expr stringInput ;
65+
66+ /**
67+ * Gets the input to this call that is the regular expression.
68+ */
69+ Expr getRegexInput ( ) { result = regexInput }
7770
7871 /**
79- * Gets the regular expression that is evaluated.
72+ * Gets the input to this call that is the string the regular expression is evaluated on .
8073 */
81- Expr getRegex ( ) { result = regex }
74+ Expr getStringInput ( ) { result = stringInput }
8275
8376 /**
84- * Gets the input string the regular expression is evaluated on .
77+ * Gets a regular expression value that is evaluated here (if any can be identified) .
8578 */
86- Expr getInput ( ) { result = input }
79+ RegExp getARegex ( ) { exists ( ParsedStringRegex regex | regex . getEval ( ) = this and result = regex ) }
8780}
8881
8982/**
@@ -94,8 +87,8 @@ private class AlwaysRegexEval extends RegexEval {
9487 this .getStaticTarget ( )
9588 .( Method )
9689 .hasQualifiedName ( "Regex" , [ "firstMatch(in:)" , "prefixMatch(in:)" , "wholeMatch(in:)" ] ) and
97- regex = this .getQualifier ( ) and
98- input = this .getArgument ( 0 ) .getExpr ( )
90+ regexInput = this .getQualifier ( ) and
91+ stringInput = this .getArgument ( 0 ) .getExpr ( )
9992 or
10093 this .getStaticTarget ( )
10194 .( Method )
@@ -107,8 +100,8 @@ private class AlwaysRegexEval extends RegexEval {
107100 "replaceMatches(in:options:range:withTemplate:)" ,
108101 "stringByReplacingMatches(in:options:range:withTemplate:)"
109102 ] ) and
110- regex = this .getQualifier ( ) and
111- input = this .getArgument ( 0 ) .getExpr ( )
103+ regexInput = this .getQualifier ( ) and
104+ stringInput = this .getArgument ( 0 ) .getExpr ( )
112105 or
113106 this .getStaticTarget ( )
114107 .( Method )
@@ -119,8 +112,8 @@ private class AlwaysRegexEval extends RegexEval {
119112 "split(separator:maxSplits:omittingEmptySubsequences:)" , "starts(with:)" ,
120113 "trimmingPrefix(_:)" , "wholeMatch(of:)"
121114 ] ) and
122- regex = this .getArgument ( 0 ) .getExpr ( ) and
123- input = this .getQualifier ( )
115+ regexInput = this .getArgument ( 0 ) .getExpr ( ) and
116+ stringInput = this .getQualifier ( )
124117 or
125118 this .getStaticTarget ( )
126119 .( Method )
@@ -131,7 +124,7 @@ private class AlwaysRegexEval extends RegexEval {
131124 "replacing(_:with:maxReplacements:)" , "replacing(_:with:subrange:maxReplacements:)" ,
132125 "trimPrefix(_:)"
133126 ] ) and
134- regex = this .getArgument ( 0 ) .getExpr ( ) and
135- input = this .getQualifier ( )
127+ regexInput = this .getArgument ( 0 ) .getExpr ( ) and
128+ stringInput = this .getQualifier ( )
136129 }
137130}
0 commit comments