Merge pull request #3627 from asger-semmle/js/unneeded-defensive-return

Approved by erik-krogh
This commit is contained in:
semmle-qlci 2020-06-25 15:28:57 +01:00 коммит произвёл GitHub
Родитель c39dce4d66 f9b796231b
Коммит cf0cd00458
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 62 добавлений и 5 удалений

Просмотреть файл

@ -63,6 +63,7 @@
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | | Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. | | Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. |
| Unneeded defensive code (`js/unneeded-defensive-code`) | Fewer false-positive results | This query now recognizes checks meant to handle the `document.all` object. |
| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. | | Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. |
| Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. | | Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. |

Просмотреть файл

@ -13,19 +13,43 @@
import javascript import javascript
import semmle.javascript.DefensiveProgramming import semmle.javascript.DefensiveProgramming
/**
* Holds if `e` looks like a check for `document.all`, which is an unusual browser object which coerces
* to `false` and has typeof `undefined`.
*/
predicate isFalsyObjectCheck(LogicalBinaryExpr e) {
exists(Variable v |
e.getAnOperand().(DefensiveExpressionTest::TypeofUndefinedTest).getOperand() = v.getAnAccess() and
e.getAnOperand().(DefensiveExpressionTest::UndefinedComparison).getOperand() = v.getAnAccess()
)
}
/**
* Holds if `e` is part of a check for `document.all`.
*/
predicate isPartOfFalsyObjectCheck(Expr e) {
exists(LogicalBinaryExpr binary |
isFalsyObjectCheck(binary) and
e = binary.getAnOperand()
)
or
isFalsyObjectCheck(e)
}
from DefensiveExpressionTest e, boolean cv from DefensiveExpressionTest e, boolean cv
where where
not isPartOfFalsyObjectCheck(e.asExpr()) and
e.getTheTestResult() = cv and e.getTheTestResult() = cv and
// whitelist // whitelist
not ( not (
// module environment detection // module environment detection
exists(VarAccess access, string name | name = "exports" or name = "module" | exists(VarAccess access, string name | name = "exports" or name = "module" |
e.asExpr().(Internal::TypeofUndefinedTest).getOperand() = access and e.asExpr().(DefensiveExpressionTest::TypeofUndefinedTest).getOperand() = access and
access.getName() = name and access.getName() = name and
not exists(access.getVariable().getADeclaration()) not exists(access.getVariable().getADeclaration())
) )
or or
// too benign in practice // too benign in practice
e instanceof Internal::DefensiveInit e instanceof DefensiveExpressionTest::DefensiveInit
) )
select e, "This guard always evaluates to " + cv + "." select e, "This guard always evaluates to " + cv + "."

Просмотреть файл

@ -14,9 +14,9 @@ abstract class DefensiveExpressionTest extends DataFlow::ValueNode {
} }
/** /**
* INTERNAL: Do not use directly; use `DefensiveExpressionTest` instead. * Provides classes for specific kinds of defensive programming patterns.
*/ */
module Internal { module DefensiveExpressionTest {
/** /**
* A defensive truthiness check that may be worth keeping, even if it * A defensive truthiness check that may be worth keeping, even if it
* is strictly speaking useless. * is strictly speaking useless.
@ -187,6 +187,13 @@ module Internal {
override Expr getOperand() { result = operand } override Expr getOperand() { result = operand }
} }
/**
* Comparison against `undefined`, such as `x === undefined`.
*/
class UndefinedComparison extends NullUndefinedComparison {
UndefinedComparison() { op2type = TTUndefined() }
}
/** /**
* An expression that throws an exception if one of its subexpressions evaluates to `null` or `undefined`. * An expression that throws an exception if one of its subexpressions evaluates to `null` or `undefined`.
* *
@ -380,7 +387,7 @@ module Internal {
/** /**
* A test for `undefined` using a `typeof` expression. * A test for `undefined` using a `typeof` expression.
* *
* Example: `typeof x === undefined'. * Example: `typeof x === "undefined"'.
*/ */
class TypeofUndefinedTest extends UndefinedNullTest { class TypeofUndefinedTest extends UndefinedNullTest {
TypeofTest test; TypeofTest test;

Просмотреть файл

@ -1,4 +1,6 @@
| module-environment-detection.js:23:8:23:36 | typeof ... efined' | This guard always evaluates to true. | | module-environment-detection.js:23:8:23:36 | typeof ... efined' | This guard always evaluates to true. |
| regression.js:9:9:9:12 | date | This guard always evaluates to true. |
| regression.js:13:25:13:40 | obj != undefined | This guard always evaluates to true. |
| tst2.js:4:12:4:35 | typeof ... efined" | This guard always evaluates to true. | | tst2.js:4:12:4:35 | typeof ... efined" | This guard always evaluates to true. |
| tst.js:18:5:18:5 | u | This guard always evaluates to false. | | tst.js:18:5:18:5 | u | This guard always evaluates to false. |
| tst.js:19:5:19:5 | n | This guard always evaluates to false. | | tst.js:19:5:19:5 | n | This guard always evaluates to false. |

Просмотреть файл

@ -0,0 +1,15 @@
function getDate() {
var date;
if (something()) {
date = new Date();
} else {
return null;
}
console.log(date);
return date && date.getTime(); // NOT OK
}
function isNotNullOrString(obj) {
return obj != null && obj != undefined && // NOT OK
typeof obj != 'string';
}

Просмотреть файл

@ -8,3 +8,11 @@
} }
}); });
}); });
const isFalsyObject = (v) => typeof v === 'undefined' && v !== undefined; // OK
function f(v) {
if (typeof v === 'undefined' && v !== undefined) { // OK
doSomething(v);
}
}