Merge branch 'main' into revertSum

This commit is contained in:
Erik Krogh Kristensen
2021-01-06 23:04:08 +01:00
39 changed files with 510 additions and 170 deletions

View File

@@ -18,6 +18,7 @@ import javascript
import DataFlow
import PathGraph
import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
/**
* A call of form `x.split(".")` where `x` is a parameter.
@@ -394,34 +395,31 @@ class InstanceOfGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::Value
*/
class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
override EqualityTest astNode;
TypeofExpr typeof;
string typeofStr;
Expr operand;
TypeofTag tag;
TypeofGuard() {
typeof = astNode.getAnOperand() and
typeofStr = astNode.getAnOperand().getStringValue()
}
TypeofGuard() { TaintTracking::isTypeofGuard(astNode, operand, tag) }
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
e = typeof.getOperand() and
e = operand and
outcome = astNode.getPolarity() and
(
typeofStr = "object" and
tag = "object" and
label = "constructor"
or
typeofStr = "function" and
tag = "function" and
label = "__proto__"
)
or
e = typeof.getOperand() and
e = operand and
outcome = astNode.getPolarity().booleanNot() and
(
// If something is not an object, sanitize object, as both must end
// in non-function prototype object.
typeofStr = "object" and
tag = "object" and
label instanceof UnsafePropLabel
or
typeofStr = "function" and
tag = "function" and
label = "constructor"
)
}

View File

@@ -328,12 +328,7 @@ module DefensiveExpressionTest {
Expr operand;
TypeofTag tag;
TypeofTest() {
exists(Expr op1, Expr op2 | hasOperands(op1, op2) |
operand = op1.(TypeofExpr).getOperand() and
op2.mayHaveStringValue(tag)
)
}
TypeofTest() { TaintTracking::isTypeofGuard(this, operand, tag) }
boolean getTheTestResult() {
exists(boolean testResult |

View File

@@ -899,12 +899,7 @@ module TaintTracking {
Expr x;
override EqualityTest astNode;
TypeOfUndefinedSanitizer() {
exists(StringLiteral str, TypeofExpr typeof | astNode.hasOperands(str, typeof) |
str.getValue() = "undefined" and
typeof.getOperand() = x
)
}
TypeOfUndefinedSanitizer() { isTypeofGuard(astNode, x, "undefined") }
override predicate sanitizes(boolean outcome, Expr e) {
outcome = astNode.getPolarity() and
@@ -914,6 +909,18 @@ module TaintTracking {
override predicate appliesTo(Configuration cfg) { any() }
}
/**
* Holds if `test` is a guard that checks if `operand` is typeof `tag`.
*
* See `TypeOfUndefinedSanitizer` for example usage.
*/
predicate isTypeofGuard(EqualityTest test, Expr operand, TypeofTag tag) {
exists(Expr str, TypeofExpr typeof | test.hasOperands(str, typeof) |
str.mayHaveStringValue(tag) and
typeof.getOperand() = operand
)
}
/** DEPRECATED. This class has been renamed to `MembershipTestSanitizer`. */
deprecated class StringInclusionSanitizer = MembershipTestSanitizer;

View File

@@ -14,6 +14,7 @@
*/
import javascript
private import semmle.javascript.dataflow.InferredTypes
module TaintedObject {
private import DataFlow
@@ -98,25 +99,24 @@ module TaintedObject {
*/
private class TypeTestGuard extends SanitizerGuard, ValueNode {
override EqualityTest astNode;
TypeofExpr typeof;
Expr operand;
boolean polarity;
TypeTestGuard() {
astNode.getAnOperand() = typeof and
(
exists(TypeofTag tag | TaintTracking::isTypeofGuard(astNode, operand, tag) |
// typeof x === "object" sanitizes `x` when it evaluates to false
astNode.getAnOperand().getStringValue() = "object" and
tag = "object" and
polarity = astNode.getPolarity().booleanNot()
or
// typeof x === "string" sanitizes `x` when it evaluates to true
astNode.getAnOperand().getStringValue() != "object" and
tag != "object" and
polarity = astNode.getPolarity()
)
}
override predicate sanitizes(boolean outcome, Expr e, FlowLabel label) {
polarity = outcome and
e = typeof.getOperand() and
e = operand and
label = label()
}
}

View File

@@ -9,6 +9,7 @@
private import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
/**
* Provides a taint tracking configuration for reasoning about
@@ -164,22 +165,18 @@ module PrototypePollutingAssignment {
private class TypeofCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
override EqualityTest astNode;
Expr operand;
string value;
boolean polarity;
TypeofCheck() {
exists(TypeofExpr typeof, Expr str |
astNode.hasOperands(typeof, str) and
typeof.getOperand() = operand and
str.getStringValue() = value
exists(TypeofTag value | TaintTracking::isTypeofGuard(astNode, operand, value) |
value = "object" and polarity = astNode.getPolarity().booleanNot()
or
value != "object" and polarity = astNode.getPolarity()
)
}
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
(
value = "object" and outcome = astNode.getPolarity().booleanNot()
or
value != "object" and outcome = astNode.getPolarity()
) and
polarity = outcome and
e = operand and
label instanceof ObjectPrototype
}

View File

@@ -134,10 +134,7 @@ module UnsafeJQueryPlugin {
SyntacticConstants::isUndefined(undef)
)
or
exists(Expr op1, Expr op2 | test.hasOperands(op1, op2) |
read.asExpr() = op1.(TypeofExpr).getOperand() and
op2.mayHaveStringValue(any(InferredType t | t = TTUndefined()).getTypeofTag())
)
TaintTracking::isTypeofGuard(test, read.asExpr(), "undefined")
)
or
polarity = true and

View File

@@ -7,6 +7,7 @@
import javascript
private import semmle.javascript.security.dataflow.RemoteFlowSources
private import semmle.javascript.PackageExports as Exports
private import semmle.javascript.dataflow.InferredTypes
/**
* Module containing sources, sinks, and sanitizers for shell command constructed from library input.
@@ -191,4 +192,20 @@ module UnsafeShellCommandConstruction {
)
}
}
/**
* A guard of the form `typeof x === "<T>"`, where <T> is "number", or "boolean",
* which sanitizes `x` in its "then" branch.
*/
class TypeOfSanitizer extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
Expr x;
override EqualityTest astNode;
TypeOfSanitizer() { TaintTracking::isTypeofGuard(astNode, x, ["number", "boolean"]) }
override predicate sanitizes(boolean outcome, Expr e) {
outcome = astNode.getPolarity() and
e = x
}
}
}

View File

@@ -98,16 +98,13 @@ module UnvalidatedDynamicMethodCall {
*/
class FunctionCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode {
override EqualityTest astNode;
TypeofExpr t;
Expr operand;
FunctionCheck() {
astNode.getAnOperand().getStringValue() = "function" and
astNode.getAnOperand().getUnderlyingValue() = t
}
FunctionCheck() { TaintTracking::isTypeofGuard(astNode, operand, "function") }
override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
outcome = astNode.getPolarity() and
e = t.getOperand().getUnderlyingValue() and
e = operand and
label instanceof MaybeNonFunction
}
}

View File

@@ -4,6 +4,7 @@
*/
import javascript
private import semmle.javascript.dataflow.InferredTypes
/**
* Classes and predicates for the XSS through DOM query.
@@ -103,25 +104,24 @@ module XssThroughDom {
*/
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
override EqualityTest astNode;
TypeofExpr typeof;
Expr operand;
boolean polarity;
TypeTestGuard() {
astNode.getAnOperand() = typeof and
(
exists(TypeofTag tag | TaintTracking::isTypeofGuard(astNode, operand, tag) |
// typeof x === "string" sanitizes `x` when it evaluates to false
astNode.getAnOperand().getStringValue() = "string" and
tag = "string" and
polarity = astNode.getPolarity().booleanNot()
or
// typeof x === "object" sanitizes `x` when it evaluates to true
astNode.getAnOperand().getStringValue() != "string" and
tag != "string" and
polarity = astNode.getPolarity()
)
}
override predicate sanitizes(boolean outcome, Expr e) {
polarity = outcome and
e = typeof.getOperand()
e = operand
}
}
}

View File

@@ -191,16 +191,16 @@ nodes
| lib/lib.js:340:22:340:26 | id(n) |
| lib/lib.js:340:22:340:26 | id(n) |
| lib/lib.js:340:25:340:25 | n |
| lib/lib.js:343:29:343:34 | unsafe |
| lib/lib.js:343:29:343:34 | unsafe |
| lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:354:20:354:23 | opts |
| lib/lib.js:354:20:354:23 | opts |
| lib/lib.js:355:20:355:23 | opts |
| lib/lib.js:355:20:355:34 | opts.learn_args |
| lib/lib.js:360:28:360:42 | this.learn_args |
| lib/lib.js:360:28:360:42 | this.learn_args |
| lib/lib.js:349:29:349:34 | unsafe |
| lib/lib.js:349:29:349:34 | unsafe |
| lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:360:20:360:23 | opts |
| lib/lib.js:360:20:360:23 | opts |
| lib/lib.js:361:20:361:23 | opts |
| lib/lib.js:361:20:361:34 | opts.learn_args |
| lib/lib.js:366:28:366:42 | this.learn_args |
| lib/lib.js:366:28:366:42 | this.learn_args |
edges
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -427,15 +427,15 @@ edges
| lib/lib.js:339:39:339:39 | n | lib/lib.js:340:25:340:25 | n |
| lib/lib.js:340:25:340:25 | n | lib/lib.js:340:22:340:26 | id(n) |
| lib/lib.js:340:25:340:25 | n | lib/lib.js:340:22:340:26 | id(n) |
| lib/lib.js:343:29:343:34 | unsafe | lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:343:29:343:34 | unsafe | lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:343:29:343:34 | unsafe | lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:343:29:343:34 | unsafe | lib/lib.js:345:22:345:27 | unsafe |
| lib/lib.js:354:20:354:23 | opts | lib/lib.js:355:20:355:23 | opts |
| lib/lib.js:354:20:354:23 | opts | lib/lib.js:355:20:355:23 | opts |
| lib/lib.js:355:20:355:23 | opts | lib/lib.js:355:20:355:34 | opts.learn_args |
| lib/lib.js:355:20:355:34 | opts.learn_args | lib/lib.js:360:28:360:42 | this.learn_args |
| lib/lib.js:355:20:355:34 | opts.learn_args | lib/lib.js:360:28:360:42 | this.learn_args |
| lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe |
| lib/lib.js:360:20:360:23 | opts | lib/lib.js:361:20:361:23 | opts |
| lib/lib.js:360:20:360:23 | opts | lib/lib.js:361:20:361:23 | opts |
| lib/lib.js:361:20:361:23 | opts | lib/lib.js:361:20:361:34 | opts.learn_args |
| lib/lib.js:361:20:361:34 | opts.learn_args | lib/lib.js:366:28:366:42 | this.learn_args |
| lib/lib.js:361:20:361:34 | opts.learn_args | lib/lib.js:366:28:366:42 | this.learn_args |
#select
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on library input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
@@ -491,5 +491,5 @@ edges
| lib/lib.js:320:11:320:26 | "rm -rf " + name | lib/lib.js:314:40:314:43 | name | lib/lib.js:320:23:320:26 | name | $@ based on library input is later used in $@. | lib/lib.js:320:11:320:26 | "rm -rf " + name | String concatenation | lib/lib.js:320:3:320:27 | cp.exec ... + name) | shell command |
| lib/lib.js:325:12:325:51 | "MyWind ... " + arg | lib/lib.js:324:40:324:42 | arg | lib/lib.js:325:49:325:51 | arg | $@ based on library input is later used in $@. | lib/lib.js:325:12:325:51 | "MyWind ... " + arg | String concatenation | lib/lib.js:326:2:326:13 | cp.exec(cmd) | shell command |
| lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | lib/lib.js:339:39:339:39 | n | lib/lib.js:340:22:340:26 | id(n) | $@ based on library input is later used in $@. | lib/lib.js:340:10:340:26 | "rm -rf " + id(n) | String concatenation | lib/lib.js:340:2:340:27 | cp.exec ... id(n)) | shell command |
| lib/lib.js:345:10:345:27 | "rm -rf " + unsafe | lib/lib.js:343:29:343:34 | unsafe | lib/lib.js:345:22:345:27 | unsafe | $@ based on library input is later used in $@. | lib/lib.js:345:10:345:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:345:2:345:28 | cp.exec ... unsafe) | shell command |
| lib/lib.js:360:17:360:56 | "learn ... + model | lib/lib.js:354:20:354:23 | opts | lib/lib.js:360:28:360:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:360:17:360:56 | "learn ... + model | String concatenation | lib/lib.js:361:3:361:18 | cp.exec(command) | shell command |
| lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | lib/lib.js:349:29:349:34 | unsafe | lib/lib.js:351:22:351:27 | unsafe | $@ based on library input is later used in $@. | lib/lib.js:351:10:351:27 | "rm -rf " + unsafe | String concatenation | lib/lib.js:351:2:351:28 | cp.exec ... unsafe) | shell command |
| lib/lib.js:366:17:366:56 | "learn ... + model | lib/lib.js:360:20:360:23 | opts | lib/lib.js:366:28:366:42 | this.learn_args | $@ based on library input is later used in $@. | lib/lib.js:366:17:366:56 | "learn ... + model | String concatenation | lib/lib.js:367:3:367:18 | cp.exec(command) | shell command |

View File

@@ -340,6 +340,12 @@ module.exports.problematic = function(n) {
cp.exec("rm -rf " + id(n)); // NOT OK
};
module.exports.typeofNumber = function(n) {
if (typeof n === "number") {
cp.exec("rm -rf " + n); // OK
}
};
function boundProblem(safe, unsafe) {
cp.exec("rm -rf " + safe); // OK
cp.exec("rm -rf " + unsafe); // NOT OK