mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge pull request #395 from esben-semmle/js/useless-defensive-code
JS: add query: js/useless-defensive-code
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.DefensiveProgramming
|
||||
|
||||
/**
|
||||
* Holds if `left` and `right` are the left and right operands, respectively, of `nd`, which is
|
||||
@@ -198,6 +199,7 @@ from ASTNode cmp,
|
||||
int leftTypeCount, int rightTypeCount ,
|
||||
string leftTypeDescription, string rightTypeDescription
|
||||
where isHeterogeneousComparison(cmp, left, right, leftTypes, rightTypes) and
|
||||
not exists (cmp.(Expr).flow().(DefensiveExpressionTest).getTheTestResult()) and
|
||||
not whitelist(left.asExpr()) and
|
||||
not whitelist(right.asExpr()) and
|
||||
leftExprDescription = capitalize(getDescription(left.asExpr(), "this expression")) and
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Defensive code can prevent unforeseen circumstances from
|
||||
causing fatal program behaviors.
|
||||
|
||||
A common defensive code pattern is to guard
|
||||
against dereferencing the values <code>null</code> or
|
||||
<code>undefined</code>.
|
||||
|
||||
However, if the situation that some defensive code guards
|
||||
against never can occur, then the defensive code serves no purpose and
|
||||
can safely be removed.
|
||||
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
|
||||
Examine the surrounding code to determine if the defensive
|
||||
code is worth keeping despite providing no practical use. If it is no
|
||||
longer needed, remove it.
|
||||
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example shows a <code>cleanupLater</code>
|
||||
function that asynchronously will perform a cleanup task after some
|
||||
delay. When the cleanup task completes, the function invokes the
|
||||
provided callback parameter <code>cb</code>.
|
||||
|
||||
To prevent a crash by invoking <code>cb</code> when it
|
||||
has the value <code>undefined</code>, defensive code guards
|
||||
the invocation by checking if <code>cb</code> is truthy.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnneededDefensiveProgramming1_bad.js" />
|
||||
|
||||
<p>
|
||||
|
||||
However, the <code>cleanupLater</code> function is always
|
||||
invoked with a callback argument, so the defensive code condition
|
||||
always holds, and it is therefore not
|
||||
required. The function can therefore be simplified to:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnneededDefensiveProgramming1_good.js" />
|
||||
|
||||
<p>
|
||||
|
||||
Guarding against the same situation multiple times is
|
||||
another example of defensive code that provides no practical use. The
|
||||
example below shows a function that assigns a value to a property of
|
||||
an object, where defensive code ensures that the assigned value is not
|
||||
<code>undefined</code> or <code>null</code>.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnneededDefensiveProgramming2_bad.js" />
|
||||
|
||||
<p>
|
||||
|
||||
However, due to coercion rules, <code>v ==
|
||||
undefined</code> holds for both the situation where <code>v</code>
|
||||
is<code>undefined</code> and the situation where <code>v</code>
|
||||
is<code>null</code>, so the <code>v == null</code>
|
||||
guard serves no purpose, and can be removed:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnneededDefensiveProgramming2_good.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Defensive_programming">Defensive programming</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Unneeded defensive code
|
||||
* @description Defensive code that guards against a situation that never happens is not needed.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id js/unneeded-defensive-code
|
||||
* @tags correctness
|
||||
* external/cwe/cwe-570
|
||||
* external/cwe/cwe-571
|
||||
* @precision very-high
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.DefensiveProgramming
|
||||
|
||||
from DefensiveExpressionTest e, boolean cv
|
||||
where e.getTheTestResult() = cv and
|
||||
// whitelist
|
||||
not (
|
||||
// module environment detection
|
||||
exists (VarAccess access, string name |
|
||||
name = "exports" or name = "module" |
|
||||
e.asExpr().(Internal::TypeofUndefinedTest).getOperand() = access and
|
||||
access.getName() = name and
|
||||
not exists (access.getVariable().getADeclaration())
|
||||
)
|
||||
or
|
||||
// too benign in practice
|
||||
e instanceof Internal::DefensiveInit
|
||||
)
|
||||
select e, "This guard always evaluates to " + cv + "."
|
||||
@@ -0,0 +1,10 @@
|
||||
function cleanupLater(delay, cb) {
|
||||
setTimeout(function() {
|
||||
cleanup();
|
||||
if (cb) { // BAD: useless check, `cb` is always truthy
|
||||
cb();
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
cleanupLater(1000, function(){console.log("Cleanup done")});
|
||||
@@ -0,0 +1,9 @@
|
||||
function cleanupLater(delay, cb) {
|
||||
setTimeout(function() {
|
||||
cleanupNow();
|
||||
// GOOD: no need to guard the invocation
|
||||
cb();
|
||||
}, delay)
|
||||
}
|
||||
|
||||
cleanupLater(function(){console.log("Cleanup done")});
|
||||
@@ -0,0 +1,5 @@
|
||||
function setSafeStringProp(o, prop, v) {
|
||||
// BAD: `v == null` is useless
|
||||
var safe = v == undefined || v == null? '': v;
|
||||
o[prop] = safe;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
function setSafeStringProp(o, prop, v) {
|
||||
// GOOD: `v == undefined` handles both `undefined` and `null`
|
||||
var safe = v == undefined? '': v;
|
||||
o[prop] = safe;
|
||||
}
|
||||
@@ -15,29 +15,7 @@
|
||||
import javascript
|
||||
import semmle.javascript.RestrictedLocations
|
||||
import semmle.javascript.dataflow.Refinements
|
||||
|
||||
/**
|
||||
* Holds if `va` is a defensive truthiness check that may be worth keeping, even if it
|
||||
* is strictly speaking useless.
|
||||
*
|
||||
* We currently recognize three patterns:
|
||||
*
|
||||
* - the first `x` in `x || (x = e)`
|
||||
* - the second `x` in `x = (x || e)`
|
||||
* - the second `x` in `var x = x || e`
|
||||
*/
|
||||
predicate isDefensiveInit(VarAccess va) {
|
||||
exists (LogOrExpr o, VarRef va2 |
|
||||
va = o.getLeftOperand().getUnderlyingReference() and va2.getVariable() = va.getVariable() |
|
||||
exists (AssignExpr assgn | va2 = assgn.getTarget() |
|
||||
assgn = o.getRightOperand().stripParens() or
|
||||
o = assgn.getRhs().getUnderlyingValue()
|
||||
) or
|
||||
exists (VariableDeclarator vd | va2 = vd.getBindingPattern() |
|
||||
o = vd.getInit().getUnderlyingValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
import semmle.javascript.DefensiveProgramming
|
||||
|
||||
/**
|
||||
* Holds if variable `v` looks like a symbolic constant, that is, it is assigned
|
||||
@@ -95,6 +73,21 @@ predicate isConstantBooleanReturnValue(Expr e) {
|
||||
isConstantBooleanReturnValue(e.(LogNotExpr).getOperand())
|
||||
}
|
||||
|
||||
private Expr maybeStripLogNot(Expr e) {
|
||||
result = maybeStripLogNot(e.(LogNotExpr).getOperand()) or
|
||||
result = e
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is a defensive expression with a fixed outcome.
|
||||
*/
|
||||
predicate isConstantDefensive(Expr e) {
|
||||
exists(DefensiveExpressionTest defensive |
|
||||
maybeStripLogNot(defensive.asExpr()) = e and
|
||||
exists(defensive.getTheTestResult())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression that should not be flagged as a useless condition.
|
||||
*
|
||||
@@ -109,7 +102,7 @@ predicate isConstantBooleanReturnValue(Expr e) {
|
||||
predicate whitelist(Expr e) {
|
||||
isConstant(e) or
|
||||
isConstant(e.(LogNotExpr).getOperand()) or
|
||||
isDefensiveInit(e) or
|
||||
isConstantDefensive(e) or // flagged by js/useless-defensive-code
|
||||
isInitialParameterUse(e) or
|
||||
isConstantBooleanReturnValue(e)
|
||||
}
|
||||
|
||||
436
javascript/ql/src/semmle/javascript/DefensiveProgramming.qll
Normal file
436
javascript/ql/src/semmle/javascript/DefensiveProgramming.qll
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* Provides classes for working with defensive programming patterns.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
/**
|
||||
* A test in a defensive programming pattern.
|
||||
*/
|
||||
abstract class DefensiveExpressionTest extends DataFlow::ValueNode {
|
||||
/** Gets the unique Boolean value that this test evaluates to, if any. */
|
||||
abstract boolean getTheTestResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use directly; use `DefensiveExpressionTest` instead.
|
||||
*/
|
||||
module Internal {
|
||||
/**
|
||||
* A defensive truthiness check that may be worth keeping, even if it
|
||||
* is strictly speaking useless.
|
||||
*
|
||||
* We currently recognize three patterns:
|
||||
*
|
||||
* - the first `x` in `x || (x = e)`
|
||||
* - the second `x` in `x = (x || e)`
|
||||
* - the second `x` in `var x = x || e`
|
||||
*/
|
||||
class DefensiveInit extends DefensiveExpressionTest {
|
||||
DefensiveInit() {
|
||||
exists(VarAccess va, LogOrExpr o, VarRef va2 |
|
||||
va = astNode and
|
||||
va = o.getLeftOperand().stripParens() and va2.getVariable() = va.getVariable() |
|
||||
exists(AssignExpr assgn | va2 = assgn.getTarget() |
|
||||
assgn = o.getRightOperand().stripParens() or
|
||||
o = assgn.getRhs().stripParens()
|
||||
)
|
||||
or
|
||||
exists(VariableDeclarator vd | va2 = vd.getBindingPattern() | o = vd.getInit().stripParens())
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
result = analyze().getTheBooleanValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inner expression of `e`, with any surrounding parentheses and boolean nots removed.
|
||||
* `polarity` is true iff the inner expression is nested in an even number of negations.
|
||||
*/
|
||||
private Expr stripNotsAndParens(Expr e, boolean polarity) {
|
||||
exists (Expr inner |
|
||||
inner = e.getUnderlyingValue() |
|
||||
if inner instanceof LogNotExpr then
|
||||
(result = stripNotsAndParens(inner.(LogNotExpr).getOperand(), polarity.booleanNot()))
|
||||
else
|
||||
(result = inner and polarity = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An equality test for `null` and `undefined`.
|
||||
*
|
||||
* Examples: `e === undefined` or `typeof e !== undefined`.
|
||||
*/
|
||||
private abstract class UndefinedNullTest extends EqualityTest {
|
||||
/** Gets the unique Boolean value that this test evaluates to, if any. */
|
||||
abstract boolean getTheTestResult();
|
||||
|
||||
/**
|
||||
* Gets the expression that is tested for being `null` or `undefined`.
|
||||
*/
|
||||
abstract Expr getOperand();
|
||||
}
|
||||
|
||||
/**
|
||||
* A dis- or conjunction that tests if an expression is `null` or `undefined` in either branch.
|
||||
*
|
||||
* Example: a branch in `x === null || x === undefined`.
|
||||
*/
|
||||
private class CompositeUndefinedNullTestPart extends DefensiveExpressionTest {
|
||||
|
||||
UndefinedNullTest test;
|
||||
|
||||
boolean polarity;
|
||||
|
||||
CompositeUndefinedNullTestPart(){
|
||||
exists (LogicalBinaryExpr composite, Variable v, Expr op, Expr opOther, UndefinedNullTest testOther |
|
||||
composite.hasOperands(op, opOther) and
|
||||
this = op.flow() and
|
||||
test = stripNotsAndParens(op, polarity) and
|
||||
testOther = stripNotsAndParens(opOther, _) and
|
||||
test.getOperand().(VarRef).getVariable() = v and
|
||||
testOther.getOperand().(VarRef).getVariable() = v
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
polarity = true and result = test.getTheTestResult()
|
||||
or
|
||||
polarity = false and result = test.getTheTestResult().booleanNot()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A test for `undefined` or `null` in an if-statement.
|
||||
*
|
||||
* Example: `if (x === null) ...`.
|
||||
*/
|
||||
private class SanityCheckingUndefinedNullGuard extends DefensiveExpressionTest {
|
||||
|
||||
UndefinedNullTest test;
|
||||
|
||||
boolean polarity;
|
||||
|
||||
SanityCheckingUndefinedNullGuard() {
|
||||
exists (IfStmt c |
|
||||
this = c.getCondition().flow() and
|
||||
test = stripNotsAndParens(c.getCondition(), polarity) and
|
||||
test.getOperand() instanceof VarRef
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
polarity = true and result = test.getTheTestResult()
|
||||
or
|
||||
polarity = false and result = test.getTheTestResult().booleanNot()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is `null` or `undefined`.
|
||||
*/
|
||||
private predicate isNullOrUndefined(InferredType t) {
|
||||
t = TTNull() or
|
||||
t = TTUndefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is not `null` or `undefined`.
|
||||
*/
|
||||
private predicate isNotNullOrUndefined(InferredType t) {
|
||||
not isNullOrUndefined(t)
|
||||
}
|
||||
|
||||
/**
|
||||
* A value comparison for `null` and `undefined`.
|
||||
*
|
||||
* Examples: `x === null` or `x != undefined`.
|
||||
*/
|
||||
private class NullUndefinedComparison extends UndefinedNullTest {
|
||||
|
||||
Expr operand;
|
||||
|
||||
InferredType op2type;
|
||||
|
||||
NullUndefinedComparison() {
|
||||
exists (Expr op2 |
|
||||
hasOperands(operand, op2) |
|
||||
op2type = TTNull() and SyntacticConstants::isNull(op2)
|
||||
or
|
||||
op2type = TTUndefined() and SyntacticConstants::isUndefined(op2)
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
result = getPolarity() and
|
||||
(
|
||||
if this instanceof StrictEqualityTest then
|
||||
// case: `operand === null` or `operand === undefined`
|
||||
operand.analyze().getTheType() = op2type
|
||||
else
|
||||
// case: `operand == null` or `operand == undefined`
|
||||
not isNotNullOrUndefined(operand.analyze().getAType())
|
||||
)
|
||||
or
|
||||
result = getPolarity().booleanNot() and
|
||||
(
|
||||
if this instanceof StrictEqualityTest then
|
||||
// case: `operand !== null` or `operand !== undefined`
|
||||
not operand.analyze().getAType() = op2type
|
||||
else
|
||||
// case: `operand != null` or `operand != undefined`
|
||||
not isNullOrUndefined(operand.analyze().getAType())
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getOperand() {
|
||||
result = operand
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that throws an exception if one of its subexpressions evaluates to `null` or `undefined`.
|
||||
*
|
||||
* Examples: `sub.p` or `sub()`.
|
||||
*/
|
||||
private class UndefinedNullCrashUse extends Expr {
|
||||
|
||||
Expr target;
|
||||
|
||||
UndefinedNullCrashUse() {
|
||||
this.(InvokeExpr).getCallee().getUnderlyingValue() = target
|
||||
or
|
||||
this.(PropAccess).getBase().getUnderlyingValue() = target
|
||||
or
|
||||
this.(MethodCallExpr).getReceiver().getUnderlyingValue() = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subexpression that will cause an exception to be thrown if it is `null` or `undefined`.
|
||||
*/
|
||||
Expr getVulnerableSubexpression() {
|
||||
result = target
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that throws an exception if one of its subexpressions is not a `function`.
|
||||
*
|
||||
* Example: `sub()`.
|
||||
*/
|
||||
private class NonFunctionCallCrashUse extends Expr {
|
||||
|
||||
Expr target;
|
||||
|
||||
NonFunctionCallCrashUse() {
|
||||
this.(InvokeExpr).getCallee().getUnderlyingValue() = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subexpression that will cause an exception to be thrown if it is not a `function`.
|
||||
*/
|
||||
Expr getVulnerableSubexpression() {
|
||||
result = target
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first expression that is guarded by `guard`.
|
||||
*/
|
||||
private Expr getAGuardedExpr(Expr guard) {
|
||||
exists(LogicalBinaryExpr op |
|
||||
op.getLeftOperand() = guard and
|
||||
op.getRightOperand() = result
|
||||
)
|
||||
or
|
||||
exists(IfStmt c, ExprStmt guardedStmt |
|
||||
c.getCondition() = guard and
|
||||
result = guardedStmt.getExpr() |
|
||||
guardedStmt = c.getAControlledStmt() or
|
||||
guardedStmt = c.getAControlledStmt().(BlockStmt).getStmt(0)
|
||||
)
|
||||
or
|
||||
exists (ConditionalExpr c |
|
||||
c.getCondition() = guard |
|
||||
result = c.getABranch()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is `string`, `number` or `boolean`.
|
||||
*/
|
||||
private predicate isStringOrNumOrBool(InferredType t) {
|
||||
t = TTString() or
|
||||
t = TTNumber() or
|
||||
t = TTBoolean()
|
||||
}
|
||||
|
||||
/**
|
||||
* A defensive expression that tests for `undefined` and `null` using a truthiness test.
|
||||
*
|
||||
* Examples: The condition in `if(x) { x.p; }` or `!x || x.m()`.
|
||||
*/
|
||||
private class UndefinedNullTruthinessGuard extends DefensiveExpressionTest {
|
||||
|
||||
VarRef guardVar;
|
||||
|
||||
boolean polarity;
|
||||
|
||||
UndefinedNullTruthinessGuard() {
|
||||
exists (VarRef useVar |
|
||||
guardVar = stripNotsAndParens(this.asExpr(), polarity) and
|
||||
guardVar.getVariable() = useVar.getVariable() |
|
||||
getAGuardedExpr(this.asExpr()).getUnderlyingValue().(UndefinedNullCrashUse).getVulnerableSubexpression() = useVar and
|
||||
// exclude types whose truthiness depend on the value
|
||||
not isStringOrNumOrBool(guardVar.analyze().getAType())
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
exists (boolean testResult |
|
||||
testResult = guardVar.analyze().getTheBooleanValue() |
|
||||
if polarity = true then
|
||||
result = testResult
|
||||
else
|
||||
result = testResult.booleanNot()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A defensive expression that tests for `undefined` and `null`.
|
||||
*
|
||||
* Example: the condition in `if(x !== null) { x.p; }`.
|
||||
*/
|
||||
private class UndefinedNullTypeGuard extends DefensiveExpressionTest {
|
||||
|
||||
UndefinedNullTest test;
|
||||
|
||||
boolean polarity;
|
||||
|
||||
UndefinedNullTypeGuard() {
|
||||
exists (Expr guard, VarRef guardVar, VarRef useVar |
|
||||
this = guard.flow() and
|
||||
test = stripNotsAndParens(guard, polarity) and
|
||||
test.getOperand() = guardVar and
|
||||
guardVar.getVariable() = useVar.getVariable() |
|
||||
getAGuardedExpr(guard).getUnderlyingValue().(UndefinedNullCrashUse).getVulnerableSubexpression() = useVar
|
||||
)
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
polarity = true and result = test.getTheTestResult()
|
||||
or
|
||||
polarity = false and result = test.getTheTestResult().booleanNot()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A test for the value of a `typeof` expression.
|
||||
*
|
||||
* Example: `typeof x === 'undefined'`.
|
||||
*/
|
||||
private class TypeofTest extends EqualityTest {
|
||||
Expr operand;
|
||||
|
||||
TypeofTag tag;
|
||||
|
||||
TypeofTest() {
|
||||
exists (Expr op1, Expr op2 |
|
||||
hasOperands(op1, op2) |
|
||||
operand = op1.(TypeofExpr).getOperand() and
|
||||
op2.mayHaveStringValue(tag)
|
||||
)
|
||||
}
|
||||
|
||||
boolean getTheTestResult() {
|
||||
exists (boolean testResult |
|
||||
testResult = true and operand.analyze().getTheType().getTypeofTag() = tag or
|
||||
testResult = false and not operand.analyze().getAType().getTypeofTag() = tag |
|
||||
if getPolarity() = true then
|
||||
result = testResult
|
||||
else
|
||||
result = testResult.booleanNot()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand used in the `typeof` expression.
|
||||
*/
|
||||
Expr getOperand() {
|
||||
result = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `typeof` tag that is tested.
|
||||
*/
|
||||
TypeofTag getTag() {
|
||||
result = tag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A defensive expression that tests if an expression has type `function`.
|
||||
*
|
||||
* Example: the condition in `if(typeof x === 'function') x()`.
|
||||
*/
|
||||
private class FunctionTypeGuard extends DefensiveExpressionTest {
|
||||
|
||||
TypeofTest test;
|
||||
|
||||
boolean polarity;
|
||||
|
||||
FunctionTypeGuard() {
|
||||
exists (Expr guard, VarRef guardVar, VarRef useVar |
|
||||
this = guard.flow() and
|
||||
test = stripNotsAndParens(guard, polarity) and
|
||||
test.getOperand() = guardVar and
|
||||
guardVar.getVariable() = useVar.getVariable() |
|
||||
getAGuardedExpr(guard).getUnderlyingValue().(NonFunctionCallCrashUse).getVulnerableSubexpression() = useVar
|
||||
) and
|
||||
test.getTag() = "function"
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
polarity = true and result = test.getTheTestResult()
|
||||
or
|
||||
polarity = false and result = test.getTheTestResult().booleanNot()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A test for `undefined` using a `typeof` expression.
|
||||
*
|
||||
* Example: `typeof x === undefined'.
|
||||
*/
|
||||
class TypeofUndefinedTest extends UndefinedNullTest {
|
||||
|
||||
TypeofTest test;
|
||||
|
||||
TypeofUndefinedTest() {
|
||||
this = test and
|
||||
test.getTag() = "undefined"
|
||||
}
|
||||
|
||||
override boolean getTheTestResult() {
|
||||
result = test.getTheTestResult()
|
||||
}
|
||||
|
||||
override Expr getOperand() {
|
||||
result = test.getOperand()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user