forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExtend.qll
More file actions
215 lines (184 loc) · 7.13 KB
/
Copy pathExtend.qll
File metadata and controls
215 lines (184 loc) · 7.13 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/**
* Provides classes for reasoning about `extend`-like functions.
*/
import javascript
/**
* A call to an `extend`-like function, which copies properties from
* one or more objects into another object, and returns the result.
*/
abstract class ExtendCall extends DataFlow::CallNode {
/**
* Gets an object from which properties are taken.
*/
abstract DataFlow::Node getASourceOperand();
/**
* Gets the object into which properties are stored, if any.
*/
abstract DataFlow::Node getDestinationOperand();
/**
* Holds if the copying operation recursively copies nested objects
* into the destination.
*/
abstract predicate isDeep();
/**
* Gets an object from which properties are taken or stored.
*/
DataFlow::Node getAnOperand() {
result = getASourceOperand() or
result = getDestinationOperand()
}
}
/** A version of `JQuery::dollarSource()` with fewer dependencies. */
private DataFlow::SourceNode localDollar() {
result.accessesGlobal(["$", "jQuery"])
or
result = DataFlow::moduleImport("jquery")
}
/**
* An extend call of form `extend(true/false, dst, src1, src2, ...)`, where the true/false
* argument is possibly omitted.
*/
private class ExtendCallWithFlag extends ExtendCall {
ExtendCallWithFlag() {
exists(string name | this = DataFlow::moduleImport(name).getACall() |
name = "extend" or
name = "extend2" or
name = "just-extend" or
name = "node.extend"
)
or
this = localDollar().getAMemberCall("extend")
}
/**
* Holds if the first argument appears to be a boolean flag.
*/
predicate hasFlag() { getArgument(0).mayHaveBooleanValue(_) }
/**
* Gets the `n`th argument after the optional boolean flag.
*/
DataFlow::Node getTranslatedArgument(int n) {
if hasFlag() then result = getArgument(n + 1) else result = getArgument(n)
}
override DataFlow::Node getASourceOperand() {
exists(int n | n >= 1 | result = getTranslatedArgument(n))
}
override DataFlow::Node getDestinationOperand() { result = getTranslatedArgument(0) }
override predicate isDeep() { getArgument(0).mayHaveBooleanValue(true) }
}
/**
* A deep extend call of form `extend(dst, src1, src2, ...)`.
*/
private class ExtendCallDeep extends ExtendCall {
ExtendCallDeep() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::moduleMember("deep", "extend") or
callee = DataFlow::moduleImport("deep-assign") or
callee = DataFlow::moduleImport("deep-extend") or
callee = DataFlow::moduleImport("deep-merge").getACall() or
callee = DataFlow::moduleImport("deepmerge") or
callee = DataFlow::moduleImport("defaults-deep") or
callee = DataFlow::moduleMember("js-extend", "extend") or
callee = DataFlow::moduleMember("merge", "recursive") or
callee = DataFlow::moduleImport("merge-deep") or
callee = DataFlow::moduleImport("merge-options") or
callee = DataFlow::moduleImport("mixin-deep") or
callee = DataFlow::moduleMember("ramda", "mergeDeepLeft") or
callee = DataFlow::moduleMember("ramda", "mergeDeepRight") or
callee = DataFlow::moduleMember("smart-extend", "deep") or
callee = LodashUnderscore::member("merge") or
callee = LodashUnderscore::member("mergeWith") or
callee = LodashUnderscore::member("defaultsDeep") or
callee = AngularJS::angular().getAPropertyRead("merge")
)
}
override DataFlow::Node getASourceOperand() { exists(int n | n >= 1 | result = getArgument(n)) }
override DataFlow::Node getDestinationOperand() { result = getArgument(0) }
override predicate isDeep() { any() }
}
/**
* A shallow extend call of form `extend(dst, src1, src2, ...)`.
*/
private class ExtendCallShallow extends ExtendCall {
ExtendCallShallow() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::globalVarRef("Object").getAPropertyRead("assign") or
callee = DataFlow::moduleImport("defaults") or
callee = DataFlow::moduleImport("extend-shallow") or
callee = DataFlow::moduleImport("merge") or
callee = DataFlow::moduleImport("mixin-object") or
callee = DataFlow::moduleImport("object-assign") or
callee = DataFlow::moduleImport("object.assign") or
callee = DataFlow::moduleImport("object.defaults") or
callee = DataFlow::moduleImport("smart-extend") or
callee = DataFlow::moduleImport("util-extend") or
callee = DataFlow::moduleImport("utils-merge") or
callee = DataFlow::moduleImport("xtend/mutable") or
callee = LodashUnderscore::member("extend") or
callee = AngularJS::angular().getAPropertyRead("extend")
)
}
override DataFlow::Node getASourceOperand() { exists(int n | n >= 1 | result = getArgument(n)) }
override DataFlow::Node getDestinationOperand() { result = getArgument(0) }
override predicate isDeep() { none() }
}
/**
* A shallow extend call of form `extend(src1, src2, ...)`.
*/
private class FunctionalExtendCallShallow extends ExtendCall {
FunctionalExtendCallShallow() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::moduleImport("xtend") or
callee = DataFlow::moduleImport("xtend/immutable") or
callee = DataFlow::moduleMember("ramda", "merge")
)
}
override DataFlow::Node getASourceOperand() { result = getAnArgument() }
override DataFlow::Node getDestinationOperand() { none() }
override predicate isDeep() { none() }
}
/**
* A taint propagating data flow edge from the objects flowing into an extend call to its return value
* and to the source of the destination object.
*/
private class ExtendCallTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(ExtendCall extend |
pred = extend.getASourceOperand() and succ = extend.getDestinationOperand().getALocalSource()
or
pred = extend.getAnOperand() and succ = extend
)
}
}
private import semmle.javascript.dataflow.internal.PreCallGraphStep
/**
* A step through a cloning library, such as `clone` or `fclone`.
*/
private class CloneStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call |
// `camelcase-keys` isn't quite a cloning library. But it's pretty close.
call = DataFlow::moduleImport(["clone", "fclone", "sort-keys", "camelcase-keys"]).getACall()
or
call = DataFlow::moduleMember("json-cycle", ["decycle", "retrocycle"]).getACall()
|
pred = call.getArgument(0) and
succ = call
)
}
}
/**
* A deep extend call from the [webpack-merge](https://npmjs.org/package/webpack-merge) library.
*/
private class WebpackMergeDeep extends ExtendCall, DataFlow::CallNode {
WebpackMergeDeep() {
this = DataFlow::moduleMember("webpack-merge", "merge").getACall()
or
this =
DataFlow::moduleMember("webpack-merge", ["mergeWithCustomize", "mergeWithRules"])
.getACall()
.getACall()
}
override DataFlow::Node getASourceOperand() { result = getAnArgument() }
override DataFlow::Node getDestinationOperand() { none() }
override predicate isDeep() { any() }
}