22 * Contains classes for recognizing array and string inclusion tests.
33 */
44
5- private import javascript
5+ private import ruby
6+ private import codeql.ruby.DataFlow
7+ private import codeql.ruby.controlflow.CfgNodes
68
79/**
810 * An expression that checks if an element is contained in an array
911 * or is a substring of another string.
1012 *
1113 * Examples:
1214 * ```
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
1718 * ```
1819 */
1920class InclusionTest extends DataFlow:: Node instanceof InclusionTest:: Range {
20- /** Gets the `A` in `A.includes (B)`. */
21+ /** Gets the `A` in `A.include? (B)`. */
2122 DataFlow:: Node getContainerNode ( ) { result = super .getContainerNode ( ) }
2223
23- /** Gets the `B` in `A.includes (B)`. */
24+ /** Gets the `B` in `A.include? (B)`. */
2425 DataFlow:: Node getContainedNode ( ) { result = super .getContainedNode ( ) }
2526
2627 /**
@@ -34,15 +35,15 @@ class InclusionTest extends DataFlow::Node instanceof InclusionTest::Range {
3435
3536module InclusionTest {
3637 /**
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)`.
3839 *
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? `.
4041 */
4142 abstract class Range extends DataFlow:: Node {
42- /** Gets the `A` in `A.includes (B)`. */
43+ /** Gets the `A` in `A.include? (B)`. */
4344 abstract DataFlow:: Node getContainerNode ( ) ;
4445
45- /** Gets the `B` in `A.includes (B)`. */
46+ /** Gets the `B` in `A.include? (B)`. */
4647 abstract DataFlow:: Node getContainedNode ( ) ;
4748
4849 /**
@@ -55,47 +56,13 @@ module InclusionTest {
5556 }
5657
5758 /**
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?`.
5961 */
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 {
9663 Includes_Native ( ) {
97- this .getMethodName ( ) = "includes " and
98- this .getNumArgument ( ) = 1
64+ this .getMethodName ( ) = "include? " and
65+ count ( this .getArgument ( _ ) ) = 1
9966 }
10067
10168 override DataFlow:: Node getContainerNode ( ) { result = this .getReceiver ( ) }
@@ -104,101 +71,60 @@ module InclusionTest {
10471 }
10572
10673 /**
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 .
10875 */
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 ]
16290 |
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
17092 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+ )
177120 )
178121 )
179122 }
180123
181- override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) . flow ( ) }
124+ override DataFlow:: Node getContainerNode ( ) { result = indexOf .getReceiver ( ) }
182125
183- override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) . flow ( ) }
126+ override DataFlow:: Node getContainedNode ( ) { result = indexOf .getArgument ( 0 ) }
184127
185128 override boolean getPolarity ( ) { result = polarity }
186129 }
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- }
204130}
0 commit comments