Skip to content

Commit 37360e7

Browse files
authored
Merge pull request #2794 from esbena/js/move-EnumeratedPropName
Approved by asgerf
2 parents 76ba48c + dcdaa96 commit 37360e7

File tree

2 files changed

+223
-217
lines changed

2 files changed

+223
-217
lines changed

javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql

Lines changed: 1 addition & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -14,157 +14,7 @@
1414
import javascript
1515
import DataFlow
1616
import PathGraph
17-
import semmle.javascript.dataflow.InferredTypes
18-
import semmle.javascript.dataflow.internal.FlowSteps
19-
20-
/**
21-
* Gets a node that refers to an element of `array`, likely obtained
22-
* as a result of enumerating the elements of the array.
23-
*/
24-
SourceNode getAnEnumeratedArrayElement(SourceNode array) {
25-
exists(MethodCallNode call, string name |
26-
call = array.getAMethodCall(name) and
27-
(name = "forEach" or name = "map") and
28-
result = call.getCallback(0).getParameter(0)
29-
)
30-
or
31-
exists(DataFlow::PropRead read |
32-
read = array.getAPropertyRead() and
33-
not exists(read.getPropertyName()) and
34-
not read.getPropertyNameExpr().analyze().getAType() = TTString() and
35-
result = read
36-
)
37-
}
38-
39-
/**
40-
* A data flow node that refers to the name of a property obtained by enumerating
41-
* the properties of some object.
42-
*/
43-
abstract class EnumeratedPropName extends DataFlow::Node {
44-
/**
45-
* Gets the data flow node holding the object whose properties are being enumerated.
46-
*
47-
* For example, gets `src` in `for (var key in src)`.
48-
*/
49-
abstract DataFlow::Node getSourceObject();
50-
51-
/**
52-
* Gets a source node that refers to the object whose properties are being enumerated.
53-
*/
54-
DataFlow::SourceNode getASourceObjectRef() {
55-
result = AccessPath::getAnAliasedSourceNode(getSourceObject())
56-
}
57-
58-
/**
59-
* Gets a property read that accesses the corresponding property value in the source object.
60-
*
61-
* For example, gets `src[key]` in `for (var key in src) { src[key]; }`.
62-
*/
63-
SourceNode getASourceProp() {
64-
exists(Node base, Node key |
65-
dynamicPropReadStep(base, key, result) and
66-
getASourceObjectRef().flowsTo(base) and
67-
key.getImmediatePredecessor*() = this
68-
)
69-
}
70-
}
71-
72-
/**
73-
* Property enumeration through `for-in` for `Object.keys` or similar.
74-
*/
75-
class ForInEnumeratedPropName extends EnumeratedPropName {
76-
DataFlow::Node object;
77-
78-
ForInEnumeratedPropName() {
79-
exists(ForInStmt stmt |
80-
this = DataFlow::lvalueNode(stmt.getLValue()) and
81-
object = stmt.getIterationDomain().flow()
82-
)
83-
or
84-
exists(CallNode call |
85-
call = globalVarRef("Object").getAMemberCall("keys")
86-
or
87-
call = globalVarRef("Object").getAMemberCall("getOwnPropertyNames")
88-
or
89-
call = globalVarRef("Reflect").getAMemberCall("ownKeys")
90-
|
91-
object = call.getArgument(0) and
92-
this = getAnEnumeratedArrayElement(call)
93-
)
94-
}
95-
96-
override Node getSourceObject() { result = object }
97-
}
98-
99-
/**
100-
* Property enumeration through `Object.entries`.
101-
*/
102-
class EntriesEnumeratedPropName extends EnumeratedPropName {
103-
CallNode entries;
104-
SourceNode entry;
105-
106-
EntriesEnumeratedPropName() {
107-
entries = globalVarRef("Object").getAMemberCall("entries") and
108-
entry = getAnEnumeratedArrayElement(entries) and
109-
this = entry.getAPropertyRead("0")
110-
}
111-
112-
override DataFlow::Node getSourceObject() {
113-
result = entries.getArgument(0)
114-
}
115-
116-
override SourceNode getASourceProp() {
117-
result = super.getASourceProp()
118-
or
119-
result = entry.getAPropertyRead("1")
120-
}
121-
}
122-
123-
/**
124-
* Gets a function that enumerates object properties when invoked.
125-
*
126-
* Invocations takes the following form:
127-
* ```js
128-
* fn(obj, (value, key, o) => { ... })
129-
* ```
130-
*/
131-
SourceNode propertyEnumerator() {
132-
result = moduleImport("for-own") or
133-
result = moduleImport("for-in") or
134-
result = moduleMember("ramda", "forEachObjIndexed") or
135-
result = LodashUnderscore::member("forEach") or
136-
result = LodashUnderscore::member("each")
137-
}
138-
139-
/**
140-
* Property enumeration through a library function taking a callback.
141-
*/
142-
class LibraryCallbackEnumeratedPropName extends EnumeratedPropName {
143-
CallNode call;
144-
FunctionNode callback;
145-
146-
LibraryCallbackEnumeratedPropName() {
147-
call = propertyEnumerator().getACall() and
148-
callback = call.getCallback(1) and
149-
this = callback.getParameter(1)
150-
}
151-
152-
override Node getSourceObject() {
153-
result = call.getArgument(0)
154-
}
155-
156-
override SourceNode getASourceObjectRef() {
157-
result = super.getASourceObjectRef()
158-
or
159-
result = callback.getParameter(2)
160-
}
161-
162-
override SourceNode getASourceProp() {
163-
result = super.getASourceProp()
164-
or
165-
result = callback.getParameter(0)
166-
}
167-
}
17+
import semmle.javascript.DynamicPropertyAccess
16818

16919
/**
17020
* Holds if the properties of `node` are enumerated locally.
@@ -173,72 +23,6 @@ predicate arePropertiesEnumerated(DataFlow::SourceNode node) {
17323
node = any(EnumeratedPropName name).getASourceObjectRef()
17424
}
17525

176-
/**
177-
* A dynamic property access that is not obviously an array access.
178-
*/
179-
class DynamicPropRead extends DataFlow::SourceNode, DataFlow::ValueNode {
180-
// Use IndexExpr instead of PropRead as we're not interested in implicit accesses like
181-
// rest-patterns and for-of loops.
182-
override IndexExpr astNode;
183-
184-
DynamicPropRead() {
185-
not exists(astNode.getPropertyName()) and
186-
// Exclude obvious array access
187-
astNode.getPropertyNameExpr().analyze().getAType() = TTString()
188-
}
189-
190-
/** Gets the base of the dynamic read. */
191-
DataFlow::Node getBase() { result = astNode.getBase().flow() }
192-
193-
/** Gets the node holding the name of the property. */
194-
DataFlow::Node getPropertyNameNode() { result = astNode.getIndex().flow() }
195-
196-
/**
197-
* Holds if the value of this read was assigned to earlier in the same basic block.
198-
*
199-
* For example, this is true for `dst[x]` on line 2 below:
200-
* ```js
201-
* dst[x] = {};
202-
* dst[x][y] = src[y];
203-
* ```
204-
*/
205-
predicate hasDominatingAssignment() {
206-
exists(DataFlow::PropWrite write, BasicBlock bb, int i, int j, SsaVariable ssaVar |
207-
write = getBase().getALocalSource().getAPropertyWrite() and
208-
bb.getNode(i) = write.getWriteNode() and
209-
bb.getNode(j) = astNode and
210-
i < j and
211-
write.getPropertyNameExpr() = ssaVar.getAUse() and
212-
astNode.getIndex() = ssaVar.getAUse()
213-
)
214-
}
215-
}
216-
217-
/**
218-
* Holds if `output` is the result of `base[key]`, either directly or through
219-
* one or more function calls, ignoring reads that can't access the prototype chain.
220-
*/
221-
predicate dynamicPropReadStep(Node base, Node key, SourceNode output) {
222-
exists(DynamicPropRead read |
223-
not read.hasDominatingAssignment() and
224-
base = read.getBase() and
225-
key = read.getPropertyNameNode() and
226-
output = read
227-
)
228-
or
229-
// Summarize functions returning a dynamic property read of two parameters, such as `function getProp(obj, prop) { return obj[prop]; }`.
230-
exists(CallNode call, Function callee, ParameterNode baseParam, ParameterNode keyParam, Node innerBase, Node innerKey, SourceNode innerOutput |
231-
dynamicPropReadStep(innerBase, innerKey, innerOutput) and
232-
baseParam.flowsTo(innerBase) and
233-
keyParam.flowsTo(innerKey) and
234-
innerOutput.flowsTo(callee.getAReturnedExpr().flow()) and
235-
call.getACallee() = callee and
236-
argumentPassingStep(call, base, callee, baseParam) and
237-
argumentPassingStep(call, key, callee, keyParam) and
238-
output = call
239-
)
240-
}
241-
24226
/**
24327
* Holds if `node` may flow from an enumerated prop name, possibly
24428
* into function calls (but not returns).

0 commit comments

Comments
 (0)