Skip to content

Commit 73af3f3

Browse files
committed
JS: Migrate PrototypePollutingFunction
1 parent ebe596f commit 73af3f3

File tree

1 file changed

+43
-50
lines changed

1 file changed

+43
-50
lines changed

javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql

Lines changed: 43 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,6 @@ string unsafePropName() {
192192
result = "constructor"
193193
}
194194

195-
/**
196-
* A flow label representing an unsafe property name, or an object obtained
197-
* by using such a property in a dynamic read.
198-
*/
199-
class UnsafePropLabel extends DataFlow::FlowLabel {
200-
UnsafePropLabel() { this = unsafePropName() }
201-
}
202-
203195
/**
204196
* Tracks data from property enumerations to dynamic property writes.
205197
*
@@ -233,49 +225,50 @@ class UnsafePropLabel extends DataFlow::FlowLabel {
233225
* a standalone configuration like in most path queries.
234226
*/
235227
module PropNameTrackingConfig implements DataFlow::StateConfigSig {
236-
class FlowState = DataFlow::FlowLabel;
228+
class FlowState extends string {
229+
FlowState() { this = unsafePropName() }
230+
}
237231

238-
predicate isSource(DataFlow::Node node, DataFlow::FlowLabel label) {
239-
label instanceof UnsafePropLabel and
232+
predicate isSource(DataFlow::Node node, FlowState state) {
233+
exists(state) and
240234
(
241235
isPollutedPropNameSource(node)
242236
or
243237
node = any(EnumeratedPropName prop).getASourceProp()
244238
)
245239
}
246240

247-
predicate isSink(DataFlow::Node node, DataFlow::FlowLabel label) {
248-
label instanceof UnsafePropLabel and
241+
predicate isSink(DataFlow::Node node, FlowState state) {
242+
exists(state) and
249243
(
250244
dynamicPropWrite(node, _, _) or
251245
dynamicPropWrite(_, node, _) or
252246
dynamicPropWrite(_, _, node)
253247
)
254248
}
255249

256-
predicate isBarrier(DataFlow::Node node, DataFlow::FlowLabel label) {
257-
node = DataFlow::MakeLabeledBarrierGuard<BarrierGuard>::getABarrierNode(label)
250+
predicate isBarrier(DataFlow::Node node, FlowState state) {
251+
node = DataFlow::MakeStateBarrierGuard<FlowState, BarrierGuard>::getABarrierNode(state)
258252
}
259253

260254
predicate isAdditionalFlowStep(
261-
DataFlow::Node pred, DataFlow::FlowLabel predlbl, DataFlow::Node succ,
262-
DataFlow::FlowLabel succlbl
255+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
263256
) {
264-
predlbl instanceof UnsafePropLabel and
265-
succlbl = predlbl and
257+
exists(state1) and
258+
state2 = state1 and
266259
(
267260
// Step through `p -> x[p]`
268261
exists(DataFlow::PropRead read |
269-
pred = read.getPropertyNameExpr().flow() and
262+
node1 = read.getPropertyNameExpr().flow() and
270263
not read.(DynamicPropRead).hasDominatingAssignment() and
271-
succ = read
264+
node2 = read
272265
)
273266
or
274267
// Step through `x -> x[p]`
275268
exists(DynamicPropRead read |
276269
not read.hasDominatingAssignment() and
277-
pred = read.getBase() and
278-
succ = read
270+
node1 = read.getBase() and
271+
node2 = read
279272
)
280273
)
281274
}
@@ -286,6 +279,8 @@ module PropNameTrackingConfig implements DataFlow::StateConfigSig {
286279
}
287280
}
288281

282+
class FlowState = PropNameTrackingConfig::FlowState;
283+
289284
module PropNameTracking = DataFlow::GlobalWithState<PropNameTrackingConfig>;
290285

291286
/**
@@ -298,9 +293,9 @@ abstract class BarrierGuard extends DataFlow::Node {
298293
predicate blocksExpr(boolean outcome, Expr e) { none() }
299294

300295
/**
301-
* Holds if this node acts as a barrier for `label`, blocking further flow from `e` if `this` evaluates to `outcome`.
296+
* Holds if this node acts as a barrier for `state`, blocking further flow from `e` if `this` evaluates to `outcome`.
302297
*/
303-
predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) { none() }
298+
predicate blocksExpr(boolean outcome, Expr e, FlowState state) { none() }
304299
}
305300

306301
/**
@@ -315,10 +310,10 @@ class DenyListEqualityGuard extends BarrierGuard, DataFlow::ValueNode {
315310
propName = unsafePropName()
316311
}
317312

318-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
313+
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
319314
e = astNode.getAnOperand() and
320315
outcome = astNode.getPolarity().booleanNot() and
321-
label = propName
316+
state = propName
322317
}
323318
}
324319

@@ -333,10 +328,9 @@ class AllowListEqualityGuard extends BarrierGuard, DataFlow::ValueNode {
333328
astNode.getAnOperand() instanceof Literal
334329
}
335330

336-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
331+
override predicate blocksExpr(boolean outcome, Expr e) {
337332
e = astNode.getAnOperand() and
338-
outcome = astNode.getPolarity() and
339-
label instanceof UnsafePropLabel
333+
outcome = astNode.getPolarity()
340334
}
341335
}
342336

@@ -382,24 +376,24 @@ class InExprGuard extends BarrierGuard, DataFlow::ValueNode {
382376
/**
383377
* A sanitizer guard for `instanceof` expressions.
384378
*
385-
* `Object.prototype instanceof X` is never true, so this blocks the `__proto__` label.
379+
* `Object.prototype instanceof X` is never true, so this blocks the `__proto__` state.
386380
*
387381
* It is still possible to get to `Function.prototype` through `constructor.constructor.prototype`
388-
* so we do not block the `constructor` label.
382+
* so we do not block the `constructor` state.
389383
*/
390384
class InstanceOfGuard extends BarrierGuard, DataFlow::ValueNode {
391385
override InstanceOfExpr astNode;
392386

393-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
394-
e = astNode.getLeftOperand() and outcome = true and label = "__proto__"
387+
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
388+
e = astNode.getLeftOperand() and outcome = true and state = "__proto__"
395389
}
396390
}
397391

398392
/**
399393
* Sanitizer guard of form `typeof x === "object"` or `typeof x === "function"`.
400394
*
401-
* The former blocks the `constructor` label as that payload must pass through a function,
402-
* and the latter blocks the `__proto__` label as that only passes through objects.
395+
* The former blocks the `constructor` state as that payload must pass through a function,
396+
* and the latter blocks the `__proto__` state as that only passes through objects.
403397
*/
404398
class TypeofGuard extends BarrierGuard, DataFlow::ValueNode {
405399
override EqualityTest astNode;
@@ -408,15 +402,15 @@ class TypeofGuard extends BarrierGuard, DataFlow::ValueNode {
408402

409403
TypeofGuard() { TaintTracking::isTypeofGuard(astNode, operand, tag) }
410404

411-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
405+
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
412406
e = operand and
413407
outcome = astNode.getPolarity() and
414408
(
415409
tag = "object" and
416-
label = "constructor"
410+
state = "constructor"
417411
or
418412
tag = "function" and
419-
label = "__proto__"
413+
state = "__proto__"
420414
)
421415
or
422416
e = operand and
@@ -425,10 +419,10 @@ class TypeofGuard extends BarrierGuard, DataFlow::ValueNode {
425419
// If something is not an object, sanitize object, as both must end
426420
// in non-function prototype object.
427421
tag = "object" and
428-
label instanceof UnsafePropLabel
422+
exists(state)
429423
or
430424
tag = "function" and
431-
label = "constructor"
425+
state = "constructor"
432426
)
433427
}
434428
}
@@ -437,19 +431,19 @@ class TypeofGuard extends BarrierGuard, DataFlow::ValueNode {
437431
* A check of form `["__proto__"].includes(x)` or similar.
438432
*/
439433
class DenyListInclusionGuard extends BarrierGuard, InclusionTest {
440-
UnsafePropLabel label;
434+
string blockedProp;
441435

442436
DenyListInclusionGuard() {
443437
exists(DataFlow::ArrayCreationNode array |
444-
array.getAnElement().getStringValue() = label and
438+
array.getAnElement().getStringValue() = blockedProp and
445439
array.flowsTo(this.getContainerNode())
446440
)
447441
}
448442

449-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
443+
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
450444
outcome = this.getPolarity().booleanNot() and
451445
e = this.getContainedNode().asExpr() and
452-
label = lbl
446+
blockedProp = state
453447
}
454448
}
455449

@@ -464,9 +458,8 @@ class AllowListInclusionGuard extends BarrierGuard {
464458
not this = any(MembershipCandidate::ObjectPropertyNameMembershipCandidate c).getTest() // handled with more precision in `HasOwnPropertyGuard`
465459
}
466460

467-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
468-
this.(TaintTracking::AdditionalBarrierGuard).blocksExpr(outcome, e) and
469-
lbl instanceof UnsafePropLabel
461+
override predicate blocksExpr(boolean outcome, Expr e) {
462+
this.(TaintTracking::AdditionalBarrierGuard).blocksExpr(outcome, e)
470463
}
471464
}
472465

@@ -482,10 +475,10 @@ class IsPlainObjectGuard extends BarrierGuard, DataFlow::CallNode {
482475
)
483476
}
484477

485-
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
478+
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
486479
e = this.getArgument(0).asExpr() and
487480
outcome = true and
488-
lbl = "constructor"
481+
state = "constructor"
489482
}
490483
}
491484

0 commit comments

Comments
 (0)