2
2
* Contains classes for recognizing array and string inclusion tests.
3
3
*/
4
4
5
- private import javascript
5
+ private import ruby
6
+ private import codeql.ruby.DataFlow
7
+ private import codeql.ruby.controlflow.CfgNodes
6
8
7
9
/**
8
10
* An expression that checks if an element is contained in an array
9
11
* or is a substring of another string.
10
12
*
11
13
* Examples:
12
14
* ```
13
- * A.includes(B)
14
- * A.indexOf(B) !== -1
15
- * A.indexOf(B) >= 0
16
- * ~A.indexOf(B)
15
+ * A.include?(B)
16
+ * A.index(B) !== -1
17
+ * A.index(B) >= 0
17
18
* ```
18
19
*/
19
20
class InclusionTest extends DataFlow:: Node instanceof InclusionTest:: Range {
20
- /** Gets the `A` in `A.includes (B)`. */
21
+ /** Gets the `A` in `A.include? (B)`. */
21
22
DataFlow:: Node getContainerNode ( ) { result = super .getContainerNode ( ) }
22
23
23
- /** Gets the `B` in `A.includes (B)`. */
24
+ /** Gets the `B` in `A.include? (B)`. */
24
25
DataFlow:: Node getContainedNode ( ) { result = super .getContainedNode ( ) }
25
26
26
27
/**
@@ -34,15 +35,15 @@ class InclusionTest extends DataFlow::Node instanceof InclusionTest::Range {
34
35
35
36
module InclusionTest {
36
37
/**
37
- * A expression that is equivalent to `A.includes (B)` or `!A.includes (B)`.
38
+ * A expression that is equivalent to `A.include? (B)` or `!A.include? (B)`.
38
39
*
39
- * Note that this also includes calls to the array method named `includes `.
40
+ * Note that this also includes calls to the array method named `include? `.
40
41
*/
41
42
abstract class Range extends DataFlow:: Node {
42
- /** Gets the `A` in `A.includes (B)`. */
43
+ /** Gets the `A` in `A.include? (B)`. */
43
44
abstract DataFlow:: Node getContainerNode ( ) ;
44
45
45
- /** Gets the `B` in `A.includes (B)`. */
46
+ /** Gets the `B` in `A.include? (B)`. */
46
47
abstract DataFlow:: Node getContainedNode ( ) ;
47
48
48
49
/**
@@ -55,47 +56,13 @@ module InclusionTest {
55
56
}
56
57
57
58
/**
58
- * A call to a utility function (`callee`) that performs an InclusionTest (`inner`).
59
+ * A call to a method named `include?`, assumed to refer to `String.include?`
60
+ * or `Array.include?`.
59
61
*/
60
- private class IndirectInclusionTest extends Range , DataFlow:: CallNode {
61
- InclusionTest inner ;
62
- Function callee ;
63
-
64
- IndirectInclusionTest ( ) {
65
- inner .getEnclosingExpr ( ) = unique( Expr ret | ret = callee .getAReturnedExpr ( ) ) and
66
- callee = unique( Function f | f = this .getACallee ( ) ) and
67
- not this .isImprecise ( ) and
68
- inner .getContainedNode ( ) .getALocalSource ( ) = DataFlow:: parameterNode ( callee .getAParameter ( ) ) and
69
- inner .getContainerNode ( ) .getALocalSource ( ) = DataFlow:: parameterNode ( callee .getAParameter ( ) )
70
- }
71
-
72
- override DataFlow:: Node getContainerNode ( ) {
73
- exists ( int arg |
74
- inner .getContainerNode ( ) .getALocalSource ( ) =
75
- DataFlow:: parameterNode ( callee .getParameter ( arg ) ) and
76
- result = this .getArgument ( arg )
77
- )
78
- }
79
-
80
- override DataFlow:: Node getContainedNode ( ) {
81
- exists ( int arg |
82
- inner .getContainedNode ( ) .getALocalSource ( ) =
83
- DataFlow:: parameterNode ( callee .getParameter ( arg ) ) and
84
- result = this .getArgument ( arg )
85
- )
86
- }
87
-
88
- override boolean getPolarity ( ) { result = inner .getPolarity ( ) }
89
- }
90
-
91
- /**
92
- * A call to a method named `includes`, assumed to refer to `String.prototype.includes`
93
- * or `Array.prototype.includes`.
94
- */
95
- private class Includes_Native extends Range , DataFlow:: MethodCallNode {
62
+ private class Includes_Native extends Range , DataFlow:: CallNode {
96
63
Includes_Native ( ) {
97
- this .getMethodName ( ) = "includes " and
98
- this .getNumArgument ( ) = 1
64
+ this .getMethodName ( ) = "include? " and
65
+ count ( this .getArgument ( _ ) ) = 1
99
66
}
100
67
101
68
override DataFlow:: Node getContainerNode ( ) { result = this .getReceiver ( ) }
@@ -104,101 +71,60 @@ module InclusionTest {
104
71
}
105
72
106
73
/**
107
- * A call to `_.includes` or similar, assumed to operate on strings .
74
+ * A check of form `A.index(B) != -1`, `A.index(B) >= 0`, or similar .
108
75
*/
109
- private class Includes_Library extends Range , DataFlow:: CallNode {
110
- Includes_Library ( ) {
111
- exists ( string name |
112
- this = LodashUnderscore:: member ( name ) .getACall ( ) and
113
- ( name = "includes" or name = "include" or name = "contains" )
114
- or
115
- this = Closure:: moduleImport ( "goog.string." + name ) .getACall ( ) and
116
- ( name = "contains" or name = "caseInsensitiveContains" )
117
- )
118
- }
119
-
120
- override DataFlow:: Node getContainerNode ( ) { result = this .getArgument ( 0 ) }
121
-
122
- override DataFlow:: Node getContainedNode ( ) { result = this .getArgument ( 1 ) }
123
- }
124
-
125
- /**
126
- * A check of form `A.indexOf(B) !== -1` or similar.
127
- */
128
- private class Includes_IndexOfEquals extends Range , DataFlow:: ValueNode {
129
- MethodCallExpr indexOf ;
130
- override EqualityTest astNode ;
131
-
132
- Includes_IndexOfEquals ( ) {
133
- exists ( Expr index | astNode .hasOperands ( indexOf , index ) |
134
- // one operand is of the form `whitelist.indexOf(x)`
135
- indexOf .getMethodName ( ) = "indexOf" and
136
- // and the other one is -1
137
- index .getIntValue ( ) = - 1
138
- )
139
- }
140
-
141
- override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) .flow ( ) }
142
-
143
- override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) .flow ( ) }
144
-
145
- override boolean getPolarity ( ) { result = astNode .getPolarity ( ) .booleanNot ( ) }
146
- }
147
-
148
- /**
149
- * A check of form `A.indexOf(B) >= 0` or similar.
150
- */
151
- private class Includes_IndexOfRelational extends Range , DataFlow:: ValueNode {
152
- MethodCallExpr indexOf ;
153
- override RelationalComparison astNode ;
154
- boolean polarity ;
155
-
156
- Includes_IndexOfRelational ( ) {
157
- exists ( Expr lesser , Expr greater |
158
- astNode .getLesserOperand ( ) = lesser and
159
- astNode .getGreaterOperand ( ) = greater and
160
- indexOf .getMethodName ( ) = "indexOf" and
161
- indexOf .getNumArgument ( ) = 1
76
+ private class Includes_IndexOfComparison extends Range , DataFlow:: Node {
77
+ private DataFlow:: CallNode indexOf ;
78
+ private boolean polarity ;
79
+
80
+ Includes_IndexOfComparison ( ) {
81
+ exists ( ExprCfgNode index , ExprNodes:: ComparisonOperationCfgNode comparison , int value |
82
+ indexOf .asExpr ( ) = comparison .getAnOperand ( ) and
83
+ index = comparison .getAnOperand ( ) and
84
+ this .asExpr ( ) = comparison and
85
+ // one operand is of the form `whitelist.index(x)`
86
+ indexOf .getMethodName ( ) = "index" and
87
+ // and the other one is 0 or -1
88
+ value = index .getConstantValue ( ) .getInt ( ) and
89
+ value = [ 0 , - 1 ]
162
90
|
163
- polarity = true and
164
- greater = indexOf and
165
- (
166
- lesser .getIntValue ( ) = 0 and astNode .isInclusive ( )
167
- or
168
- lesser .getIntValue ( ) = - 1 and not astNode .isInclusive ( )
169
- )
91
+ value = - 1 and polarity = false and comparison .getExpr ( ) instanceof CaseEqExpr
170
92
or
171
- polarity = false and
172
- lesser = indexOf and
173
- (
174
- greater .getIntValue ( ) = - 1 and astNode .isInclusive ( )
175
- or
176
- greater .getIntValue ( ) = 0 and not astNode .isInclusive ( )
93
+ value = - 1 and polarity = false and comparison .getExpr ( ) instanceof EqExpr
94
+ or
95
+ value = - 1 and polarity = true and comparison .getExpr ( ) instanceof NEExpr
96
+ or
97
+ value = 0 and polarity = false and comparison .getExpr ( ) instanceof NEExpr
98
+ or
99
+ exists ( RelationalOperation op | op = comparison .getExpr ( ) |
100
+ exists ( Expr lesser , Expr greater |
101
+ op .getLesserOperand ( ) = lesser and
102
+ op .getGreaterOperand ( ) = greater
103
+ |
104
+ polarity = true and
105
+ greater = indexOf .asExpr ( ) .getExpr ( ) and
106
+ (
107
+ value = 0 and op .isInclusive ( )
108
+ or
109
+ value = - 1 and not op .isInclusive ( )
110
+ )
111
+ or
112
+ polarity = false and
113
+ lesser = indexOf .asExpr ( ) .getExpr ( ) and
114
+ (
115
+ value = - 1 and op .isInclusive ( )
116
+ or
117
+ value = 0 and not op .isInclusive ( )
118
+ )
119
+ )
177
120
)
178
121
)
179
122
}
180
123
181
- override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) . flow ( ) }
124
+ override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) }
182
125
183
- override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) . flow ( ) }
126
+ override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) }
184
127
185
128
override boolean getPolarity ( ) { result = polarity }
186
129
}
187
-
188
- /**
189
- * An expression of form `~A.indexOf(B)` which, when coerced to a boolean, is equivalent to `A.includes(B)`.
190
- */
191
- private class Includes_IndexOfBitwise extends Range , DataFlow:: ValueNode {
192
- MethodCallExpr indexOf ;
193
- override BitNotExpr astNode ;
194
-
195
- Includes_IndexOfBitwise ( ) {
196
- astNode .getOperand ( ) = indexOf and
197
- indexOf .getMethodName ( ) = "indexOf"
198
- }
199
-
200
- override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) .flow ( ) }
201
-
202
- override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) .flow ( ) }
203
- }
204
130
}
0 commit comments