Skip to content

Commit 6eb88b9

Browse files
committed
introduce and use TaintTracking::isTypeofGuard
1 parent 34a6e15 commit 6eb88b9

9 files changed

Lines changed: 53 additions & 66 deletions

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import javascript
1818
import DataFlow
1919
import PathGraph
2020
import semmle.javascript.DynamicPropertyAccess
21+
private import semmle.javascript.dataflow.InferredTypes
2122

2223
/**
2324
* A call of form `x.split(".")` where `x` is a parameter.
@@ -394,34 +395,31 @@ class InstanceOfGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::Value
394395
*/
395396
class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
396397
override EqualityTest astNode;
397-
TypeofExpr typeof;
398-
string typeofStr;
398+
Expr operand;
399+
InferredType type;
399400

400-
TypeofGuard() {
401-
typeof = astNode.getAnOperand() and
402-
typeofStr = astNode.getAnOperand().getStringValue()
403-
}
401+
TypeofGuard() { TaintTracking::isTypeofGuard(astNode, operand, type) }
404402

405403
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
406-
e = typeof.getOperand() and
404+
e = operand and
407405
outcome = astNode.getPolarity() and
408406
(
409-
typeofStr = "object" and
407+
type = TTObject() and
410408
label = "constructor"
411409
or
412-
typeofStr = "function" and
410+
type = TTFunction() and
413411
label = "__proto__"
414412
)
415413
or
416-
e = typeof.getOperand() and
414+
e = operand and
417415
outcome = astNode.getPolarity().booleanNot() and
418416
(
419417
// If something is not an object, sanitize object, as both must end
420418
// in non-function prototype object.
421-
typeofStr = "object" and
419+
type = TTObject() and
422420
label instanceof UnsafePropLabel
423421
or
424-
typeofStr = "function" and
422+
type = TTFunction() and
425423
label = "constructor"
426424
)
427425
}

javascript/ql/src/semmle/javascript/DefensiveProgramming.qll

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -326,20 +326,15 @@ module DefensiveExpressionTest {
326326
*/
327327
private class TypeofTest extends EqualityTest {
328328
Expr operand;
329-
TypeofTag tag;
329+
InferredType type;
330330

331-
TypeofTest() {
332-
exists(Expr op1, Expr op2 | hasOperands(op1, op2) |
333-
operand = op1.(TypeofExpr).getOperand() and
334-
op2.mayHaveStringValue(tag)
335-
)
336-
}
331+
TypeofTest() { TaintTracking::isTypeofGuard(this, operand, type) }
337332

338333
boolean getTheTestResult() {
339334
exists(boolean testResult |
340-
testResult = true and operand.analyze().getTheType().getTypeofTag() = tag
335+
testResult = true and operand.analyze().getTheType() = type
341336
or
342-
testResult = false and not operand.analyze().getAType().getTypeofTag() = tag
337+
testResult = false and not operand.analyze().getAType() = type
343338
|
344339
if getPolarity() = true then result = testResult else result = testResult.booleanNot()
345340
)
@@ -353,7 +348,7 @@ module DefensiveExpressionTest {
353348
/**
354349
* Gets the `typeof` tag that is tested.
355350
*/
356-
TypeofTag getTag() { result = tag }
351+
TypeofTag getTag() { result = type.getTypeofTag() }
357352
}
358353

359354
/**

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -899,12 +899,7 @@ module TaintTracking {
899899
Expr x;
900900
override EqualityTest astNode;
901901

902-
TypeOfUndefinedSanitizer() {
903-
exists(StringLiteral str, TypeofExpr typeof | astNode.hasOperands(str, typeof) |
904-
str.getValue() = "undefined" and
905-
typeof.getOperand() = x
906-
)
907-
}
902+
TypeOfUndefinedSanitizer() { isTypeofGuard(astNode, x, TTUndefined()) }
908903

909904
override predicate sanitizes(boolean outcome, Expr e) {
910905
outcome = astNode.getPolarity() and
@@ -914,6 +909,18 @@ module TaintTracking {
914909
override predicate appliesTo(Configuration cfg) { any() }
915910
}
916911

912+
/**
913+
* Holds if `test` is a guard that checks if `operand` is typeof `type`.
914+
*
915+
* See `TypeOfUndefinedSanitizer` for example usage.
916+
*/
917+
predicate isTypeofGuard(EqualityTest test, Expr operand, InferredType type) {
918+
exists(Expr str, TypeofExpr typeof | test.hasOperands(str, typeof) |
919+
str.mayHaveStringValue(type.getTypeofTag()) and
920+
typeof.getOperand() = operand
921+
)
922+
}
923+
917924
/** DEPRECATED. This class has been renamed to `MembershipTestSanitizer`. */
918925
deprecated class StringInclusionSanitizer = MembershipTestSanitizer;
919926

javascript/ql/src/semmle/javascript/security/TaintedObject.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
import javascript
17+
private import semmle.javascript.dataflow.InferredTypes
1718

1819
module TaintedObject {
1920
private import DataFlow
@@ -98,25 +99,24 @@ module TaintedObject {
9899
*/
99100
private class TypeTestGuard extends SanitizerGuard, ValueNode {
100101
override EqualityTest astNode;
101-
TypeofExpr typeof;
102+
Expr operand;
102103
boolean polarity;
103104

104105
TypeTestGuard() {
105-
astNode.getAnOperand() = typeof and
106-
(
106+
exists(InferredType type | TaintTracking::isTypeofGuard(astNode, operand, type) |
107107
// typeof x === "object" sanitizes `x` when it evaluates to false
108-
astNode.getAnOperand().getStringValue() = "object" and
108+
type = TTObject() and
109109
polarity = astNode.getPolarity().booleanNot()
110110
or
111111
// typeof x === "string" sanitizes `x` when it evaluates to true
112-
astNode.getAnOperand().getStringValue() != "object" and
112+
type != TTObject() and
113113
polarity = astNode.getPolarity()
114114
)
115115
}
116116

117117
override predicate sanitizes(boolean outcome, Expr e, FlowLabel label) {
118118
polarity = outcome and
119-
e = typeof.getOperand() and
119+
e = operand and
120120
label = label()
121121
}
122122
}

javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollutingAssignment.qll

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
private import javascript
1111
private import semmle.javascript.DynamicPropertyAccess
12+
private import semmle.javascript.dataflow.InferredTypes
1213

1314
/**
1415
* Provides a taint tracking configuration for reasoning about
@@ -164,22 +165,18 @@ module PrototypePollutingAssignment {
164165
private class TypeofCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
165166
override EqualityTest astNode;
166167
Expr operand;
167-
string value;
168+
boolean polarity;
168169

169170
TypeofCheck() {
170-
exists(TypeofExpr typeof, Expr str |
171-
astNode.hasOperands(typeof, str) and
172-
typeof.getOperand() = operand and
173-
str.getStringValue() = value
171+
exists(InferredType type | TaintTracking::isTypeofGuard(astNode, operand, type) |
172+
type = TTObject() and polarity = astNode.getPolarity().booleanNot()
173+
or
174+
type != TTObject() and polarity = astNode.getPolarity()
174175
)
175176
}
176177

177178
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
178-
(
179-
value = "object" and outcome = astNode.getPolarity().booleanNot()
180-
or
181-
value != "object" and outcome = astNode.getPolarity()
182-
) and
179+
polarity = outcome and
183180
e = operand and
184181
label instanceof ObjectPrototype
185182
}

javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPluginCustomizations.qll

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,7 @@ module UnsafeJQueryPlugin {
134134
SyntacticConstants::isUndefined(undef)
135135
)
136136
or
137-
exists(Expr op1, Expr op2 | test.hasOperands(op1, op2) |
138-
read.asExpr() = op1.(TypeofExpr).getOperand() and
139-
op2.mayHaveStringValue(any(InferredType t | t = TTUndefined()).getTypeofTag())
140-
)
137+
TaintTracking::isTypeofGuard(test, read.asExpr(), TTUndefined())
141138
)
142139
or
143140
polarity = true and

javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,8 @@ module UnsafeShellCommandConstruction {
200200
override EqualityTest astNode;
201201

202202
TypeOfSanitizer() {
203-
exists(Expr str, TypeofExpr typeof | astNode.hasOperands(str, typeof) |
204-
str.mayHaveStringValue(any(InferredType t |
205-
t = TTUndefined() or t = TTNumber() or t = TTBoolean()
206-
).getTypeofTag()) and
207-
typeof.getOperand() = x
208-
)
203+
TaintTracking::isTypeofGuard(astNode, x,
204+
any(InferredType t | t = TTUndefined() or t = TTNumber() or t = TTBoolean()))
209205
}
210206

211207
override predicate sanitizes(boolean outcome, Expr e) {

javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCallCustomizations.qll

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,13 @@ module UnvalidatedDynamicMethodCall {
9898
*/
9999
class FunctionCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
100100
override EqualityTest astNode;
101-
TypeofExpr t;
101+
Expr operand;
102102

103-
FunctionCheck() {
104-
astNode.getAnOperand().getStringValue() = "function" and
105-
astNode.getAnOperand().getUnderlyingValue() = t
106-
}
103+
FunctionCheck() { TaintTracking::isTypeofGuard(astNode, operand, TTFunction()) }
107104

108105
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
109106
outcome = astNode.getPolarity() and
110-
e = t.getOperand().getUnderlyingValue() and
107+
e = operand and
111108
label instanceof MaybeNonFunction
112109
}
113110
}

javascript/ql/src/semmle/javascript/security/dataflow/XssThroughDom.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import javascript
7+
private import semmle.javascript.dataflow.InferredTypes
78

89
/**
910
* Classes and predicates for the XSS through DOM query.
@@ -103,25 +104,24 @@ module XssThroughDom {
103104
*/
104105
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
105106
override EqualityTest astNode;
106-
TypeofExpr typeof;
107+
Expr operand;
107108
boolean polarity;
108109

109110
TypeTestGuard() {
110-
astNode.getAnOperand() = typeof and
111-
(
111+
exists(InferredType type | TaintTracking::isTypeofGuard(astNode, operand, type) |
112112
// typeof x === "string" sanitizes `x` when it evaluates to false
113-
astNode.getAnOperand().getStringValue() = "string" and
113+
type = TTString() and
114114
polarity = astNode.getPolarity().booleanNot()
115115
or
116116
// typeof x === "object" sanitizes `x` when it evaluates to true
117-
astNode.getAnOperand().getStringValue() != "string" and
117+
type != TTString() and
118118
polarity = astNode.getPolarity()
119119
)
120120
}
121121

122122
override predicate sanitizes(boolean outcome, Expr e) {
123123
polarity = outcome and
124-
e = typeof.getOperand()
124+
e = operand
125125
}
126126
}
127127
}

0 commit comments

Comments
 (0)