Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c185ced

Browse files
committed
JS: More pruning and more data flow
1 parent 4b89eee commit c185ced

3 files changed

Lines changed: 581 additions & 201 deletions

File tree

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

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import javascript
1515
import DataFlow
1616
import PathGraph
1717
import semmle.javascript.dataflow.InferredTypes
18+
import semmle.javascript.dataflow.internal.FlowSteps
1819

1920
/**
2021
* Gets a node that refers to an element of `array`, likely obtained
@@ -52,9 +53,12 @@ abstract class EnumeratedPropName extends DataFlow::Node {
5253
*
5354
* For example, gets `src[key]` in `for (var key in src) { src[key]; }`.
5455
*/
55-
PropRead getASourceProp() {
56-
result = AccessPath::getAnAliasedSourceNode(getSourceObject()).getAPropertyRead() and
57-
result.getPropertyNameExpr().flow().getImmediatePredecessor*() = this
56+
SourceNode getASourceProp() {
57+
exists(Node base, Node key |
58+
dynamicPropReadStep(base, key, result) and
59+
AccessPath::getAnAliasedSourceNode(getSourceObject()).flowsTo(base) and
60+
key.getImmediatePredecessor*() = this
61+
)
5862
}
5963
}
6064

@@ -102,7 +106,7 @@ class EntriesEnumeratedPropName extends EnumeratedPropName {
102106
result = entries.getArgument(0)
103107
}
104108

105-
override PropRead getASourceProp() {
109+
override SourceNode getASourceProp() {
106110
result = super.getASourceProp()
107111
or
108112
result = entry.getAPropertyRead("1")
@@ -133,6 +137,9 @@ class DynamicPropRead extends DataFlow::SourceNode, DataFlow::ValueNode {
133137
/** Gets the base of the dynamic read. */
134138
DataFlow::Node getBase() { result = astNode.getBase().flow() }
135139

140+
/** Gets the node holding the name of the property. */
141+
DataFlow::Node getPropertyNameNode() { result = astNode.getIndex().flow() }
142+
136143
/**
137144
* Holds if the value of this read was assigned to earlier in the same basic block.
138145
*
@@ -154,6 +161,72 @@ class DynamicPropRead extends DataFlow::SourceNode, DataFlow::ValueNode {
154161
}
155162
}
156163

164+
/**
165+
* Holds if `output` is the result of `base[key]`, either directly or through
166+
* one or more function calls.
167+
*/
168+
predicate dynamicPropReadStep(Node base, Node key, SourceNode output) {
169+
exists(DynamicPropRead read |
170+
not read.hasDominatingAssignment() and
171+
base = read.getBase() and
172+
key = read.getPropertyNameNode() and
173+
output = read
174+
)
175+
or
176+
// Summarize functions returning a dynamic property read of two parameters.
177+
exists(CallNode call, Function callee, ParameterNode baseParam, ParameterNode keyParam, Node innerBase, Node innerKey, SourceNode innerOutput |
178+
dynamicPropReadStep(innerBase, innerKey, innerOutput) and
179+
baseParam.flowsTo(innerBase) and
180+
keyParam.flowsTo(innerKey) and
181+
innerOutput.flowsTo(callee.getAReturnedExpr().flow()) and
182+
call.getACallee() = callee and
183+
argumentPassing(call, base, callee, baseParam) and
184+
argumentPassing(call, key, callee, keyParam) and
185+
output = call
186+
)
187+
}
188+
189+
/**
190+
* Holds if `node` may flow from an enumerated prop name, possibly
191+
* into function calls (but not returns).
192+
*/
193+
predicate isEnumeratedPropName(Node node) {
194+
node instanceof EnumeratedPropName
195+
or
196+
exists(Node pred |
197+
isEnumeratedPropName(pred)
198+
|
199+
node = pred.getASuccessor()
200+
or
201+
argumentPassing(_, pred, _, node)
202+
or
203+
// Handle one level of callbacks
204+
exists(FunctionNode function, ParameterNode callback, int i |
205+
pred = callback.getAnInvocation().getArgument(i) and
206+
argumentPassing(_, function, _, callback) and
207+
node = function.getParameter(i)
208+
)
209+
)
210+
}
211+
212+
/**
213+
* Holds if `node` may refer to `Object.prototype` obtained through dynamic property
214+
* read of a property obtained through property enumeration.
215+
*/
216+
predicate isPotentiallyObjectPrototype(SourceNode node) {
217+
exists(Node base, Node key |
218+
dynamicPropReadStep(base, key, node) and
219+
isEnumeratedPropName(key) and
220+
not arePropertiesEnumerated(base.getALocalSource()) // ignore `for (let key in src) { ... src[key] ... }`
221+
)
222+
or
223+
exists(Node use |
224+
isPotentiallyObjectPrototype(use.getALocalSource())
225+
|
226+
argumentPassing(_, use, _, node)
227+
)
228+
}
229+
157230
/**
158231
* Holds if there is a dynamic property assignment of form `base[prop] = rhs`
159232
* which might act as the writing operation in a recursive merge function.
@@ -168,6 +241,7 @@ predicate dynamicPropWrite(DataFlow::Node base, DataFlow::Node prop, DataFlow::N
168241
exists(AssignExpr write, IndexExpr index |
169242
index = write.getLhs() and
170243
base = index.getBase().flow() and
244+
isPotentiallyObjectPrototype(base.getALocalSource()) and
171245
prop = index.getPropertyNameExpr().flow() and
172246
rhs = write.getRhs().flow() and
173247
not exists(prop.getStringValue()) and

0 commit comments

Comments
 (0)