mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #7892 from erik-krogh/nanSan
JS: Add a `isNaN` sanitizer, and use it in queries that already had a typeof check
This commit is contained in:
@@ -1166,6 +1166,36 @@ module TaintTracking {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `guard` is a test that checks if `operand` is a number. */
|
||||
predicate isNumberGuard(DataFlow::Node guard, Expr operand, boolean polarity) {
|
||||
exists(DataFlow::CallNode isNaN |
|
||||
isNaN = DataFlow::globalVarRef("isNaN").getACall() and guard = isNaN and polarity = false
|
||||
|
|
||||
operand = isNaN.getArgument(0).asExpr()
|
||||
or
|
||||
exists(DataFlow::CallNode parse |
|
||||
parse = DataFlow::globalVarRef(["parseInt", "parseFloat"]).getACall()
|
||||
|
|
||||
parse = isNaN.getArgument(0) and
|
||||
operand = parse.getArgument(0).asExpr()
|
||||
)
|
||||
or
|
||||
exists(UnaryExpr unary | unary.getOperator() = ["+", "-"] |
|
||||
unary = isNaN.getArgument(0).asExpr() and
|
||||
operand = unary.getOperand()
|
||||
)
|
||||
or
|
||||
exists(BinaryExpr bin | bin.getOperator() = ["+", "-"] |
|
||||
bin = isNaN.getArgument(0).asExpr() and
|
||||
operand = bin.getAnOperand() and
|
||||
bin.getAnOperand() instanceof NumberLiteral
|
||||
)
|
||||
)
|
||||
or
|
||||
isTypeofGuard(guard.asExpr(), operand, "number") and
|
||||
polarity = guard.asExpr().(EqualityTest).getPolarity()
|
||||
}
|
||||
|
||||
/** DEPRECATED. This class has been renamed to `MembershipTestSanitizer`. */
|
||||
deprecated class StringInclusionSanitizer = MembershipTestSanitizer;
|
||||
|
||||
|
||||
@@ -110,6 +110,16 @@ module TaintedObject {
|
||||
}
|
||||
}
|
||||
|
||||
/** A guard that checks whether `x` is a number. */
|
||||
class NumberGuard extends SanitizerGuard instanceof DataFlow::CallNode {
|
||||
Expr x;
|
||||
boolean polarity;
|
||||
|
||||
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer guard that validates an input against a JSON schema.
|
||||
*/
|
||||
|
||||
@@ -122,6 +122,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
guard instanceof InstanceofCheck or
|
||||
guard instanceof IsArrayCheck or
|
||||
guard instanceof TypeofCheck or
|
||||
guard instanceof NumberGuard or
|
||||
guard instanceof EqualityCheck or
|
||||
guard instanceof IncludesCheck
|
||||
}
|
||||
@@ -228,6 +229,16 @@ private class TypeofCheck extends TaintTracking::LabeledSanitizerGuardNode, Data
|
||||
}
|
||||
}
|
||||
|
||||
/** A guard that checks whether `x` is a number. */
|
||||
class NumberGuard extends TaintTracking::SanitizerGuardNode instanceof DataFlow::CallNode {
|
||||
Expr x;
|
||||
boolean polarity;
|
||||
|
||||
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
|
||||
/** A call to `Array.isArray`, which is false for `Object.prototype`. */
|
||||
private class IsArrayCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::CallNode {
|
||||
IsArrayCheck() { this = DataFlow::globalVarRef("Array").getAMemberCall("isArray") }
|
||||
|
||||
@@ -153,6 +153,16 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/** A guard that checks whether `x` is a number. */
|
||||
class NumberGuard extends TaintTracking::SanitizerGuardNode instanceof DataFlow::CallNode {
|
||||
Expr x;
|
||||
boolean polarity;
|
||||
|
||||
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
|
||||
/**
|
||||
* The client-provided options object for a jQuery plugin, considered as a source for unsafe jQuery plugins.
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,8 @@ class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
|
||||
super.isSanitizerGuard(node) or
|
||||
node instanceof IsElementSanitizer or
|
||||
node instanceof PropertyPresenceSanitizer
|
||||
node instanceof PropertyPresenceSanitizer or
|
||||
node instanceof NumberGuard
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,16 @@ module UnsafeShellCommandConstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/** A guard that checks whether `x` is a number. */
|
||||
class NumberGuard extends TaintTracking::SanitizerGuardNode instanceof DataFlow::CallNode {
|
||||
Expr x;
|
||||
boolean polarity;
|
||||
|
||||
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
|
||||
private import semmle.javascript.dataflow.internal.AccessPaths
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
|
||||
guard instanceof PathExistsSanitizerGuard or
|
||||
guard instanceof TaintTracking::AdHocWhitelistCheckSanitizer
|
||||
guard instanceof TaintTracking::AdHocWhitelistCheckSanitizer or
|
||||
guard instanceof NumberGuard or
|
||||
guard instanceof TypeOfSanitizer
|
||||
}
|
||||
|
||||
// override to require that there is a path without unmatched return steps
|
||||
|
||||
@@ -108,4 +108,14 @@ module UnvalidatedDynamicMethodCall {
|
||||
label instanceof MaybeNonFunction
|
||||
}
|
||||
}
|
||||
|
||||
/** A guard that checks whether `x` is a number. */
|
||||
class NumberGuard extends TaintTracking::SanitizerGuardNode instanceof DataFlow::CallNode {
|
||||
Expr x;
|
||||
boolean polarity;
|
||||
|
||||
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node nd) { super.isSanitizer(nd) }
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
|
||||
guard instanceof NumberGuard or
|
||||
guard instanceof FunctionCheck
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel,
|
||||
DataFlow::FlowLabel dstlabel
|
||||
|
||||
@@ -28,6 +28,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
|
||||
guard instanceof TypeTestGuard or
|
||||
guard instanceof UnsafeJQuery::PropertyPresenceSanitizer or
|
||||
guard instanceof UnsafeJQuery::NumberGuard or
|
||||
guard instanceof DomBasedXss::SanitizerGuard
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,24 @@ nodes
|
||||
| lib/lib.js:498:45:498:48 | name |
|
||||
| lib/lib.js:499:31:499:34 | name |
|
||||
| lib/lib.js:499:31:499:34 | name |
|
||||
| lib/lib.js:509:39:509:42 | name |
|
||||
| lib/lib.js:509:39:509:42 | name |
|
||||
| lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:545:23:545:26 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
|
||||
| lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
@@ -574,6 +592,38 @@ edges
|
||||
| lib/lib.js:498:45:498:48 | name | lib/lib.js:499:31:499:34 | name |
|
||||
| lib/lib.js:498:45:498:48 | name | lib/lib.js:499:31:499:34 | name |
|
||||
| lib/lib.js:498:45:498:48 | name | lib/lib.js:499:31:499:34 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:510:22:510:25 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:513:23:513:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:519:23:519:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:525:23:525:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:531:23:531:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
@@ -666,6 +716,14 @@ edges
|
||||
| lib/lib.js:478:27:478:46 | config.installedPath | lib/lib.js:477:33:477:38 | config | lib/lib.js:478:27:478:46 | config.installedPath | $@ based on $@ is later used in $@. | lib/lib.js:478:27:478:46 | config.installedPath | Path concatenation | lib/lib.js:477:33:477:38 | config | library input | lib/lib.js:479:12:479:20 | exec(cmd) | shell command |
|
||||
| lib/lib.js:483:13:483:33 | ' my na ... + name | lib/lib.js:482:40:482:43 | name | lib/lib.js:483:30:483:33 | name | $@ based on $@ is later used in $@. | lib/lib.js:483:13:483:33 | ' my na ... + name | String concatenation | lib/lib.js:482:40:482:43 | name | library input | lib/lib.js:485:2:485:20 | cp.exec(cmd + args) | shell command |
|
||||
| lib/lib.js:499:19:499:34 | "rm -rf " + name | lib/lib.js:498:45:498:48 | name | lib/lib.js:499:31:499:34 | name | $@ based on $@ is later used in $@. | lib/lib.js:499:19:499:34 | "rm -rf " + name | String concatenation | lib/lib.js:498:45:498:48 | name | library input | lib/lib.js:499:3:499:35 | MyThing ... + name) | shell command |
|
||||
| lib/lib.js:510:10:510:25 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:510:22:510:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:510:10:510:25 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:510:2:510:26 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:513:11:513:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:513:23:513:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:513:11:513:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:513:3:513:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:519:11:519:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:519:23:519:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:519:11:519:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:519:3:519:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:525:11:525:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:525:23:525:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:525:11:525:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:525:3:525:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:531:11:531:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:531:23:531:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:531:11:531:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:531:3:531:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:537:11:537:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:537:11:537:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:537:3:537:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:543:11:543:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:543:11:543:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:543:3:543:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:545:11:545:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name | $@ based on $@ is later used in $@. | lib/lib.js:545:11:545:26 | "rm -rf " + name | String concatenation | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:545:3:545:27 | cp.exec ... + name) | shell command |
|
||||
| lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | $@ based on $@ is later used in $@. | lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | String concatenation | lib/subLib2/compiled-file.ts:3:26:3:29 | name | library input | lib/subLib2/compiled-file.ts:4:5:4:29 | cp.exec ... + name) | shell command |
|
||||
| lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib2/special-file.js:3:28:3:31 | name | library input | lib/subLib2/special-file.js:4:2:4:26 | cp.exec ... + name) | shell command |
|
||||
| lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | String concatenation | lib/subLib3/my-file.ts:3:28:3:31 | name | library input | lib/subLib3/my-file.ts:4:2:4:26 | cp.exec ... + name) | shell command |
|
||||
|
||||
@@ -504,4 +504,44 @@ module.exports.myCommand = function (myCommand) {
|
||||
var imp = require('./isImported');
|
||||
for (var name in imp){
|
||||
module.exports[name] = imp[name];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.sanitizer4 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (isNaN(name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(name))) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(+name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(name, 10))) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(name - 0)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(name | 0)) { // <- not a sanitizer
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user