Skip to content

Commit a9e89ed

Browse files
committed
JS: Migrate PrototypePollutingAssignment
1 parent bcc1669 commit a9e89ed

File tree

2 files changed

+71
-37
lines changed

2 files changed

+71
-37
lines changed

javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentCustomizations.qll

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,37 @@ private import javascript
1010
* that my cause prototype pollution.
1111
*/
1212
module PrototypePollutingAssignment {
13+
private newtype TFlowState =
14+
TTaint() or
15+
TObjectPrototype()
16+
17+
/** A flow state to associate with a tracked value. */
18+
class FlowState extends TFlowState {
19+
/** Gets a string representation fo this flow state */
20+
string toString() {
21+
this = TTaint() and result = "taint"
22+
or
23+
this = TObjectPrototype() and result = "object-prototype"
24+
}
25+
26+
deprecated DataFlow::FlowLabel toFlowLabel() {
27+
this = TTaint() and result.isTaint()
28+
or
29+
this = TObjectPrototype() and result instanceof ObjectPrototype
30+
}
31+
}
32+
33+
/** Predicates for working with flow states. */
34+
module FlowState {
35+
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
36+
37+
/** A tainted value. */
38+
FlowState taint() { result = TTaint() }
39+
40+
/** A reference to `Object.prototype` obtained by reading from a tainted property name. */
41+
FlowState objectPrototype() { result = TObjectPrototype() }
42+
}
43+
1344
/**
1445
* A data flow source for untrusted data from which the special `__proto__` property name may be arise.
1546
*/
@@ -30,7 +61,10 @@ module PrototypePollutingAssignment {
3061
* Use the `taint` label for untrusted property names, and the `ObjectPrototype` label for
3162
* object mutations.
3263
*/
33-
abstract DataFlow::FlowLabel getAFlowLabel();
64+
FlowState getAFlowState() { result = FlowState::objectPrototype() }
65+
66+
/** DEPRECATED. Use `getAFlowState()` instead. */
67+
deprecated DataFlow::FlowLabel getAFlowLabel() { result = this.getAFlowState().toFlowLabel() }
3468
}
3569

3670
/**
@@ -48,16 +82,16 @@ module PrototypePollutingAssignment {
4882
predicate blocksExpr(boolean outcome, Expr e) { none() }
4983

5084
/**
51-
* Holds if this node acts as a barrier for `label`, blocking further flow from `e` if `this` evaluates to `outcome`.
85+
* Holds if this node acts as a barrier for `state`, blocking further flow from `e` if `this` evaluates to `outcome`.
5286
*/
53-
predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) { none() }
87+
predicate blocksExpr(boolean outcome, Expr e, FlowState state) { none() }
5488

5589
/** DEPRECATED. Use `blocksExpr` instead. */
5690
deprecated predicate sanitizes(boolean outcome, Expr e) { this.blocksExpr(outcome, e) }
5791

5892
/** DEPRECATED. Use `blocksExpr` instead. */
5993
deprecated predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
60-
this.blocksExpr(outcome, e, label)
94+
this.blocksExpr(outcome, e, FlowState::fromFlowLabel(label))
6195
}
6296
}
6397

@@ -74,7 +108,7 @@ module PrototypePollutingAssignment {
74108
}
75109

76110
/** A flow label representing the `Object.prototype` value. */
77-
abstract class ObjectPrototype extends DataFlow::FlowLabel {
111+
abstract deprecated class ObjectPrototype extends DataFlow::FlowLabel {
78112
ObjectPrototype() { this = "Object.prototype" }
79113
}
80114

@@ -90,7 +124,7 @@ module PrototypePollutingAssignment {
90124
this = any(DeleteExpr del).getOperand().flow().(DataFlow::PropRef).getBase()
91125
}
92126

93-
override DataFlow::FlowLabel getAFlowLabel() { result instanceof ObjectPrototype }
127+
override FlowState getAFlowState() { result = FlowState::objectPrototype() }
94128
}
95129

96130
/** A remote flow source or location.{hash,search} as a taint source. */

javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,23 @@ private import javascript
1111
private import semmle.javascript.DynamicPropertyAccess
1212
private import semmle.javascript.dataflow.InferredTypes
1313
import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment
14+
private import PrototypePollutingAssignmentCustomizations::PrototypePollutingAssignment as PrototypePollutingAssignment
1415
private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles
1516

1617
// Materialize flow labels
17-
private class ConcreteObjectPrototype extends ObjectPrototype {
18+
deprecated private class ConcreteObjectPrototype extends ObjectPrototype {
1819
ConcreteObjectPrototype() { this = this }
1920
}
2021

2122
/** A taint-tracking configuration for reasoning about prototype-polluting assignments. */
2223
module PrototypePollutingAssignmentConfig implements DataFlow::StateConfigSig {
23-
class FlowState = DataFlow::FlowLabel;
24+
class FlowState = PrototypePollutingAssignment::FlowState;
2425

25-
predicate isSource(DataFlow::Node node, DataFlow::FlowLabel label) {
26-
node instanceof Source and label.isTaint()
26+
predicate isSource(DataFlow::Node node, FlowState label) {
27+
node instanceof Source and label = FlowState::taint()
2728
}
2829

29-
predicate isSink(DataFlow::Node node, DataFlow::FlowLabel lbl) {
30-
node.(Sink).getAFlowLabel() = lbl
31-
}
30+
predicate isSink(DataFlow::Node node, FlowState lbl) { node.(Sink).getAFlowState() = lbl }
3231

3332
predicate isBarrier(DataFlow::Node node) {
3433
node instanceof Sanitizer
@@ -59,28 +58,28 @@ module PrototypePollutingAssignmentConfig implements DataFlow::StateConfigSig {
5958
node = DataFlow::MakeBarrierGuard<BarrierGuard>::getABarrierNode()
6059
}
6160

62-
predicate isBarrierOut(DataFlow::Node node, DataFlow::FlowLabel lbl) {
61+
predicate isBarrierOut(DataFlow::Node node, FlowState lbl) {
6362
// Suppress the value-preserving step src -> dst in `extend(dst, src)`. This is modeled as a value-preserving
6463
// step because it preserves all properties, but the destination is not actually Object.prototype.
6564
node = any(ExtendCall call).getASourceOperand() and
66-
lbl instanceof ObjectPrototype
65+
lbl = FlowState::objectPrototype()
6766
}
6867

69-
predicate isBarrierIn(DataFlow::Node node, DataFlow::FlowLabel lbl) {
68+
predicate isBarrierIn(DataFlow::Node node, FlowState lbl) {
7069
// FIXME: This should only be an in-barrier for the corresponding flow state, but flow-state specific in-barriers are not supported right now.
7170
isSource(node, lbl)
7271
}
7372

7473
predicate isAdditionalFlowStep(
75-
DataFlow::Node pred, DataFlow::FlowLabel inlbl, DataFlow::Node succ, DataFlow::FlowLabel outlbl
74+
DataFlow::Node pred, FlowState inlbl, DataFlow::Node succ, FlowState outlbl
7675
) {
7776
// Step from x -> obj[x] while switching to the ObjectPrototype label
7877
// (If `x` can have the value `__proto__` then the result can be Object.prototype)
7978
exists(DynamicPropRead read |
8079
pred = read.getPropertyNameNode() and
8180
succ = read and
82-
inlbl.isTaint() and
83-
outlbl instanceof ObjectPrototype and
81+
inlbl = FlowState::taint() and
82+
outlbl = FlowState::objectPrototype() and
8483
// Exclude cases where the property name came from a property enumeration.
8584
// If the property name is an own property of the base object, the read won't
8685
// return Object.prototype.
@@ -96,31 +95,31 @@ module PrototypePollutingAssignmentConfig implements DataFlow::StateConfigSig {
9695
proj.isSingletonProjection() and
9796
pred = proj.getASelector() and
9897
succ = proj and
99-
inlbl.isTaint() and
100-
outlbl instanceof ObjectPrototype
98+
inlbl = FlowState::taint() and
99+
outlbl = FlowState::objectPrototype()
101100
)
102101
or
103102
// TODO: local field step becomes a jump step, resulting in FPs (closure-lib)
104103
// TODO: localFieldStep is too expensive with dataflow2
105104
// DataFlow::localFieldStep(pred, succ)
106105
none()
107106
or
108-
inlbl.isTaint() and
107+
inlbl = FlowState::taint() and
109108
TaintTracking::defaultTaintStep(pred, succ) and
110109
inlbl = outlbl
111110
}
112111

113112
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
114113

115-
predicate isBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {
116-
lbl.isTaint() and
114+
predicate isBarrier(DataFlow::Node node, FlowState lbl) {
115+
lbl = FlowState::taint() and
117116
TaintTracking::defaultSanitizer(node)
118117
or
119118
// Don't propagate into the receiver, as the method lookups will generally fail on Object.prototype.
120119
node instanceof DataFlow::ThisNode and
121-
lbl instanceof ObjectPrototype
120+
lbl = FlowState::objectPrototype()
122121
or
123-
node = DataFlow::MakeLabeledBarrierGuard<BarrierGuard>::getABarrierNode(lbl)
122+
node = DataFlow::MakeStateBarrierGuard<FlowState, BarrierGuard>::getABarrierNode(lbl)
124123
}
125124
}
126125

@@ -173,7 +172,8 @@ deprecated class Configuration extends TaintTracking::Configuration {
173172
override predicate isAdditionalFlowStep(
174173
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
175174
) {
176-
PrototypePollutingAssignmentConfig::isAdditionalFlowStep(pred, inlbl, succ, outlbl)
175+
PrototypePollutingAssignmentConfig::isAdditionalFlowStep(pred, FlowState::fromFlowLabel(inlbl),
176+
succ, FlowState::fromFlowLabel(outlbl))
177177
}
178178

179179
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
@@ -264,10 +264,10 @@ private class PropertyPresenceCheck extends BarrierGuard, DataFlow::ValueNode {
264264
not isPropertyPresentOnObjectPrototype(astNode.getPropertyName())
265265
}
266266

267-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
267+
override predicate blocksExpr(boolean outcome, Expr e, FlowState label) {
268268
e = astNode.getBase() and
269269
outcome = true and
270-
label instanceof ObjectPrototype
270+
label = FlowState::objectPrototype()
271271
}
272272
}
273273

@@ -279,21 +279,21 @@ private class InExprCheck extends BarrierGuard, DataFlow::ValueNode {
279279
not isPropertyPresentOnObjectPrototype(astNode.getLeftOperand().getStringValue())
280280
}
281281

282-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
282+
override predicate blocksExpr(boolean outcome, Expr e, FlowState label) {
283283
e = astNode.getRightOperand() and
284284
outcome = true and
285-
label instanceof ObjectPrototype
285+
label = FlowState::objectPrototype()
286286
}
287287
}
288288

289289
/** A check of form `e instanceof X`, which is always false for `Object.prototype`. */
290290
private class InstanceofCheck extends BarrierGuard, DataFlow::ValueNode {
291291
override InstanceofExpr astNode;
292292

293-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
293+
override predicate blocksExpr(boolean outcome, Expr e, FlowState label) {
294294
e = astNode.getLeftOperand() and
295295
outcome = true and
296-
label instanceof ObjectPrototype
296+
label = FlowState::objectPrototype()
297297
}
298298
}
299299

@@ -311,10 +311,10 @@ private class TypeofCheck extends BarrierGuard, DataFlow::ValueNode {
311311
)
312312
}
313313

314-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
314+
override predicate blocksExpr(boolean outcome, Expr e, FlowState label) {
315315
polarity = outcome and
316316
e = operand and
317-
label instanceof ObjectPrototype
317+
label = FlowState::objectPrototype()
318318
}
319319
}
320320

@@ -332,10 +332,10 @@ class NumberGuard extends BarrierGuard instanceof DataFlow::CallNode {
332332
private class IsArrayCheck extends BarrierGuard, DataFlow::CallNode {
333333
IsArrayCheck() { this = DataFlow::globalVarRef("Array").getAMemberCall("isArray") }
334334

335-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
335+
override predicate blocksExpr(boolean outcome, Expr e, FlowState label) {
336336
e = this.getArgument(0).asExpr() and
337337
outcome = true and
338-
label instanceof ObjectPrototype
338+
label = FlowState::objectPrototype()
339339
}
340340
}
341341

0 commit comments

Comments
 (0)