mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #4862 from erik-krogh/shellSanitizer
Approved by esbena
This commit is contained in:
@@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,10 +191,10 @@ 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: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 |
|
||||
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 |
|
||||
@@ -421,10 +421,10 @@ 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: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 |
|
||||
#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 |
|
||||
@@ -480,4 +480,4 @@ 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: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 |
|
||||
|
||||
@@ -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
|
||||
@@ -349,4 +355,4 @@ Object.defineProperty(module.exports, "boundProblem", {
|
||||
get: function () {
|
||||
return boundProblem.bind(this, "safe");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user