-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathStringOps.qll
More file actions
180 lines (154 loc) · 5.37 KB
/
StringOps.qll
File metadata and controls
180 lines (154 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
* Provides classes and predicates for reasoning about string-manipulating expressions.
*/
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.controlflow.CfgNodes
private import InclusionTests
/**
* Provides classes for reasoning about string-manipulating expressions.
*/
module StringOps {
/**
* A expression that is equivalent to `A.start_with?(B)` or `!A.start_with?(B)`.
*/
class StartsWith extends DataFlow::Node instanceof StartsWith::Range {
/**
* Gets the `A` in `A.start_with?(B)`.
*/
final DataFlow::Node getBaseString() { result = super.getBaseString() }
/**
* Gets the `B` in `A.start_with?(B)`.
*/
final DataFlow::Node getSubstring() { result = super.getSubstring() }
/**
* Gets the polarity of the check.
*
* If the polarity is `false` the check returns `true` if the string does not start
* with the given substring.
*/
final boolean getPolarity() { result = super.getPolarity() }
}
/**
* Provides classes implementing prefix test expressions.
*/
module StartsWith {
/**
* A expression that is equivalent to `A.start_with?(B)` or `!A.start_with?(B)`.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the `A` in `A.start_with?(B)`.
*/
abstract DataFlow::Node getBaseString();
/**
* Gets the `B` in `A.start_with?(B)`.
*/
abstract DataFlow::Node getSubstring();
/**
* Gets the polarity of the check.
*
* If the polarity is `false` the check returns `true` if the string does not start
* with the given substring.
*/
boolean getPolarity() { result = true }
}
/**
* An expression of form `A.start_with?(B)`.
*/
private class StartsWith_Native extends Range, DataFlow::CallNode {
StartsWith_Native() { this.getMethodName() = "start_with?" }
override DataFlow::Node getBaseString() { result = this.getReceiver() }
override DataFlow::Node getSubstring() { result = this.getArgument(_) }
}
/**
* An expression of form `A.index(B) == 0` or `A.index(B) != 0`.
*/
private class StartsWith_IndexOfEquals extends Range {
private DataFlow::CallNode indexOf;
private boolean polarity;
StartsWith_IndexOfEquals() {
exists(ExprNodes::ComparisonOperationCfgNode comparison |
this.asExpr() = comparison and
indexOf.getMethodName() = "index" and
strictcount(indexOf.getArgument(_)) = 1 and
indexOf.flowsTo(any(DataFlow::Node n | n.asExpr() = comparison.getAnOperand())) and
comparison.getAnOperand().getConstantValue().getInt() = 0
|
polarity = true and comparison.getExpr() instanceof EqExpr
or
polarity = true and comparison.getExpr() instanceof CaseEqExpr
or
polarity = false and comparison.getExpr() instanceof NEExpr
)
}
override DataFlow::Node getBaseString() { result = indexOf.getReceiver() }
override DataFlow::Node getSubstring() { result = indexOf.getArgument(0) }
override boolean getPolarity() { result = polarity }
}
}
/**
* An expression that is equivalent to `A.include?(B)` or `!A.include?(B)`.
* Note that this class is equivalent to `InclusionTest`, which also matches
* inclusion tests on array objects.
*/
class Includes extends InclusionTest {
/** Gets the `A` in `A.include?(B)`. */
final DataFlow::Node getBaseString() { result = super.getContainerNode() }
/** Gets the `B` in `A.include?(B)`. */
final DataFlow::Node getSubstring() { result = super.getContainedNode() }
}
/**
* An expression that is equivalent to `A.end_with?(B)` or `!A.end_with?(B)`.
*/
class EndsWith extends DataFlow::Node instanceof EndsWith::Range {
/**
* Gets the `A` in `A.start_with?(B)`.
*/
final DataFlow::Node getBaseString() { result = super.getBaseString() }
/**
* Gets the `B` in `A.start_with?(B)`.
*/
final DataFlow::Node getSubstring() { result = super.getSubstring() }
/**
* Gets the polarity if the check.
*
* If the polarity is `false` the check returns `true` if the string does not end
* with the given substring.
*/
final boolean getPolarity() { result = super.getPolarity() }
}
/**
* Provides classes implementing suffix test expressions.
*/
module EndsWith {
/**
* An expression that is equivalent to `A.end_with?(B)` or `!A.end_with?(B)`.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the `A` in `A.end_with?(B)`.
*/
abstract DataFlow::Node getBaseString();
/**
* Gets the `B` in `A.end_with?(B)`.
*/
abstract DataFlow::Node getSubstring();
/**
* Gets the polarity if the check.
*
* If the polarity is `false` the check returns `true` if the string does not end
* with the given substring.
*/
boolean getPolarity() { result = true }
}
/**
* An expression of form `A.end_with?(B)`.
*/
private class EndsWith_Native extends Range, DataFlow::CallNode {
EndsWith_Native() { this.getMethodName() = "end_with?" }
override DataFlow::Node getBaseString() { result = this.getReceiver() }
override DataFlow::Node getSubstring() { result = this.getArgument(_) }
}
}
}