mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge master into next.
This commit is contained in:
49
javascript/ql/src/Declarations/DeadStore.qhelp
Normal file
49
javascript/ql/src/Declarations/DeadStore.qhelp
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
A value is assigned to a variable or property, but either that location is never read
|
||||
later on, or its value is always overwritten before being read. This means
|
||||
that the original assignment has no effect, and could indicate a logic error or
|
||||
incomplete code.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that you check the control and data flow in the method carefully.
|
||||
If a value is really not needed, consider omitting the assignment. Be careful,
|
||||
though: if the right-hand side has a side-effect (like performing a method call),
|
||||
it is important to keep this to preserve the overall behavior.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following example, the return value of the call to <code>send</code> on line 2
|
||||
is assigned to the local variable <code>result</code>, but then never used.
|
||||
</p>
|
||||
|
||||
<sample src="examples/DeadStoreOfLocal.js" />
|
||||
|
||||
<p>
|
||||
Assuming that <code>send</code> returns a status code indicating whether the operation
|
||||
succeeded or not, the value of <code>result</code> should be checked, perhaps like this:
|
||||
</p>
|
||||
|
||||
<sample src="examples/DeadStoreOfLocalGood.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Dead_store">Dead store</a>.</li>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
22
javascript/ql/src/Declarations/DeadStore.qll
Normal file
22
javascript/ql/src/Declarations/DeadStore.qll
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about dead stores.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression that may be used as a default initial value,
|
||||
* such as `0` or `-1`, or an empty object or array literal.
|
||||
*/
|
||||
predicate isDefaultInit(Expr e) {
|
||||
// primitive default values: zero, false, empty string, and (integer) -1
|
||||
e.(NumberLiteral).getValue().toFloat() = 0.0 or
|
||||
e.(NegExpr).getOperand().(NumberLiteral).getValue() = "1" or
|
||||
e.(ConstantString).getStringValue() = "" or
|
||||
e.(BooleanLiteral).getValue() = "false" or
|
||||
// initialising to an empty array or object literal, even if unnecessary,
|
||||
// can convey useful type information to the reader
|
||||
e.(ArrayExpr).getSize() = 0 or
|
||||
e.(ObjectExpr).getNumProperty() = 0 or
|
||||
SyntacticConstants::isNullOrUndefined(e)
|
||||
}
|
||||
@@ -1,49 +1,9 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
A value is assigned to a local variable, but either that variable is never read
|
||||
later on, or its value is always overwritten before being read. This means
|
||||
that the original assignment has no effect, and could indicate a logic error or
|
||||
incomplete code.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<include src="DeadStore.qhelp" />
|
||||
|
||||
<p>
|
||||
Ensure that you check the control and data flow in the method carefully.
|
||||
If a value is really not needed, consider omitting the assignment. Be careful,
|
||||
though: if the right-hand side has a side-effect (like performing a method call),
|
||||
it is important to keep this to preserve the overall behavior.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following example, the return value of the call to <code>send</code> on line 2
|
||||
is assigned to the local variable <code>result</code>, but then never used.
|
||||
</p>
|
||||
|
||||
<sample src="examples/DeadStoreOfLocal.js" />
|
||||
|
||||
<p>
|
||||
Assuming that <code>send</code> returns a status code indicating whether the operation
|
||||
succeeded or not, the value of <code>result</code> should be checked, perhaps like this:
|
||||
</p>
|
||||
|
||||
<sample src="examples/DeadStoreOfLocalGood.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Dead_store">Dead store</a>.</li>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DeadStore
|
||||
|
||||
/**
|
||||
* Holds if `vd` is a definition of variable `v` that is dead, that is,
|
||||
@@ -25,22 +26,6 @@ predicate deadStoreOfLocal(VarDef vd, PurelyLocalVariable v) {
|
||||
not exists (SsaExplicitDefinition ssa | ssa.defines(vd, v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression that may be used as a default initial value,
|
||||
* such as `0` or `-1`, or an empty object or array literal.
|
||||
*/
|
||||
predicate isDefaultInit(Expr e) {
|
||||
// primitive default values: zero, false, empty string, and (integer) -1
|
||||
e.(NumberLiteral).getValue().toFloat() = 0.0 or
|
||||
e.(NegExpr).getOperand().(NumberLiteral).getValue() = "1" or
|
||||
e.(ConstantString).getStringValue() = "" or
|
||||
e.(BooleanLiteral).getValue() = "false" or
|
||||
// initialising to an empty array or object literal, even if unnecessary,
|
||||
// can convey useful type information to the reader
|
||||
e.(ArrayExpr).getSize() = 0 or
|
||||
e.(ObjectExpr).getNumProperty() = 0
|
||||
}
|
||||
|
||||
from VarDef dead, PurelyLocalVariable v // captured variables may be read by closures, so don't flag them
|
||||
where deadStoreOfLocal(dead, v) and
|
||||
// the variable should be accessed somewhere; otherwise it will be flagged by UnusedVariable
|
||||
|
||||
9
javascript/ql/src/Declarations/DeadStoreOfProperty.qhelp
Normal file
9
javascript/ql/src/Declarations/DeadStoreOfProperty.qhelp
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
|
||||
<qhelp>
|
||||
|
||||
<include src="DeadStore.qhelp" />
|
||||
|
||||
</qhelp>
|
||||
142
javascript/ql/src/Declarations/DeadStoreOfProperty.ql
Normal file
142
javascript/ql/src/Declarations/DeadStoreOfProperty.ql
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @name Useless assignment to property
|
||||
* @description An assignment to a property whose value is always overwritten has no effect.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/useless-assignment-to-property
|
||||
* @tags maintainability
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import Expressions.DOMProperties
|
||||
import DeadStore
|
||||
|
||||
/**
|
||||
* Holds if `write` writes to property `name` of `base`, and `base` is the only base object of `write`.
|
||||
*/
|
||||
predicate unambiguousPropWrite(DataFlow::SourceNode base, string name, DataFlow::PropWrite write) {
|
||||
write = base.getAPropertyWrite(name) and
|
||||
not exists (DataFlow::SourceNode otherBase |
|
||||
otherBase != base and
|
||||
write = otherBase.getAPropertyWrite(name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `assign1` and `assign2` both assign property `name` of the same object, and `assign2` post-dominates `assign1`.
|
||||
*/
|
||||
predicate postDominatedPropWrite(string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2) {
|
||||
exists (ControlFlowNode write1, ControlFlowNode write2, DataFlow::SourceNode base, ReachableBasicBlock block1, ReachableBasicBlock block2 |
|
||||
write1 = assign1.getWriteNode() and
|
||||
write2 = assign2.getWriteNode() and
|
||||
block1 = write1.getBasicBlock() and
|
||||
block2 = write2.getBasicBlock() and
|
||||
unambiguousPropWrite(base, name, assign1) and
|
||||
unambiguousPropWrite(base, name, assign2) and
|
||||
block2.postDominates(block1) and
|
||||
(block1 = block2 implies
|
||||
exists (int i1, int i2 |
|
||||
write1 = block1.getNode(i1) and
|
||||
write2 = block2.getNode(i2) and
|
||||
i1 < i2
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` may access a property named `name`.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate maybeAccessesProperty(Expr e, string name) {
|
||||
(e.(PropAccess).getPropertyName() = name and e instanceof RValue) or
|
||||
// conservatively reject all side-effects
|
||||
e.isImpure()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `assign1` and `assign2` both assign property `name`, but `assign1` is dead because of `assign2`.
|
||||
*/
|
||||
predicate isDeadAssignment(string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2) {
|
||||
postDominatedPropWrite(name, assign1, assign2) and
|
||||
noPropAccessBetween(name, assign1, assign2) and
|
||||
not isDOMProperty(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `assign` assigns a property `name` that may be accessed somewhere else in the same block,
|
||||
* `after` indicates if the access happens before or after the node for `assign`.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate maybeAccessesAssignedPropInBlock(string name, DataFlow::PropWrite assign, boolean after) {
|
||||
exists (ControlFlowNode write, ReachableBasicBlock block, int i, int j, Expr e |
|
||||
write = assign.getWriteNode() and
|
||||
block = assign.getBasicBlock() and
|
||||
write = block.getNode(i) and
|
||||
e = block.getNode(j) and
|
||||
maybeAccessesProperty(e, name) |
|
||||
after = true and i < j
|
||||
or
|
||||
after = false and j < i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `assign1` and `assign2` both assign property `name`, and the assigned property is not accessed between the two assignments.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate noPropAccessBetween(string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2) {
|
||||
exists (ControlFlowNode write1, ControlFlowNode write2, ReachableBasicBlock block1, ReachableBasicBlock block2 |
|
||||
write1 = assign1.getWriteNode() and
|
||||
write2 = assign2.getWriteNode() and
|
||||
write1.getBasicBlock() = block1 and
|
||||
write2.getBasicBlock() = block2 and
|
||||
if block1 = block2 then
|
||||
// same block: check for access between
|
||||
not exists (int i1, Expr mid, int i2 |
|
||||
assign1.getWriteNode() = block1.getNode(i1) and
|
||||
assign2.getWriteNode() = block2.getNode(i2) and
|
||||
mid = block1.getNode([i1+1..i2-1]) and
|
||||
maybeAccessesProperty(mid, name)
|
||||
)
|
||||
else
|
||||
// other block:
|
||||
not (
|
||||
// check for an access after the first write node
|
||||
maybeAccessesAssignedPropInBlock(name, assign1, true) or
|
||||
// check for an access between the two write blocks
|
||||
exists (ReachableBasicBlock mid |
|
||||
block1.getASuccessor+() = mid and
|
||||
mid.getASuccessor+() = block2 |
|
||||
maybeAccessesProperty(mid.getANode(), name)
|
||||
) or
|
||||
// check for an access before the second write node
|
||||
maybeAccessesAssignedPropInBlock(name, assign2, false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2
|
||||
where isDeadAssignment(name, assign1, assign2) and
|
||||
// whitelist
|
||||
not (
|
||||
// Google Closure Compiler pattern: `o.p = o['p'] = v`
|
||||
exists (PropAccess p1, PropAccess p2 |
|
||||
p1 = assign1.getAstNode() and
|
||||
p2 = assign2.getAstNode() |
|
||||
p1 instanceof DotExpr and p2 instanceof IndexExpr
|
||||
or
|
||||
p2 instanceof DotExpr and p1 instanceof IndexExpr
|
||||
)
|
||||
or
|
||||
// don't flag overwrites for default values
|
||||
isDefaultInit(assign1.getRhs().asExpr().getUnderlyingValue())
|
||||
or
|
||||
// don't flag assignments in externs
|
||||
assign1.getAstNode().inExternsFile()
|
||||
or
|
||||
// exclude result from js/overwritten-property
|
||||
assign2.getBase() instanceof DataFlow::ObjectLiteralNode
|
||||
)
|
||||
select assign1.getWriteNode(), "This write to property '" + name + "' is useless, since $@ always overrides it.", assign2.getWriteNode(), "another property write"
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -459,6 +459,11 @@ module DataFlow {
|
||||
prop = getPropertyName() and
|
||||
rhs = getRhs()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node where the property write happens in the control flow graph.
|
||||
*/
|
||||
abstract ControlFlowNode getWriteNode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,6 +489,10 @@ module DataFlow {
|
||||
override Node getRhs() {
|
||||
result = valueNode(astNode.(LValue).getRhs())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = astNode.(LValue).getDefNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,6 +517,10 @@ module DataFlow {
|
||||
override Node getRhs() {
|
||||
result = valueNode(prop.(ValueProperty).getInit())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = prop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -537,6 +550,10 @@ module DataFlow {
|
||||
propdesc.hasPropertyWrite("value", result)
|
||||
)
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = odp.getAstNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,6 +580,10 @@ module DataFlow {
|
||||
not prop instanceof AccessorMethodDefinition and
|
||||
result = valueNode(prop.getInit())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = prop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,6 +608,10 @@ module DataFlow {
|
||||
override Node getRhs() {
|
||||
result = valueNode(prop.getValue())
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = prop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -869,6 +894,11 @@ module DataFlow {
|
||||
override Node getBase() {
|
||||
result = valueNode(arr)
|
||||
}
|
||||
|
||||
override ControlFlowNode getWriteNode() {
|
||||
result = arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.CallGraph
|
||||
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
/**
|
||||
@@ -809,6 +810,74 @@ module TaintTracking {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that returns the result of a sanitizer check.
|
||||
*/
|
||||
private class SanitizingFunction extends Function {
|
||||
Parameter sanitizedParameter;
|
||||
|
||||
SanitizerGuardNode sanitizer;
|
||||
|
||||
boolean sanitizerOutcome;
|
||||
|
||||
SanitizingFunction() {
|
||||
exists(Expr e |
|
||||
exists(Expr returnExpr |
|
||||
returnExpr = sanitizer.asExpr()
|
||||
or
|
||||
// ad hoc support for conjunctions:
|
||||
returnExpr.(LogAndExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = true
|
||||
or
|
||||
// ad hoc support for disjunctions:
|
||||
returnExpr.(LogOrExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = false
|
||||
|
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.getDef().getSource() = returnExpr and
|
||||
ssa.getVariable().getAUse() = getAReturnedExpr()
|
||||
)
|
||||
or
|
||||
returnExpr = getAReturnedExpr()
|
||||
) and
|
||||
DataFlow::parameterNode(sanitizedParameter).flowsToExpr(e) and
|
||||
sanitizer.sanitizes(sanitizerOutcome, e)
|
||||
) and
|
||||
getNumParameter() = 1 and
|
||||
sanitizedParameter = getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
|
||||
*/
|
||||
predicate isSanitizingCall(DataFlow::CallNode call, Expr e, boolean outcome) {
|
||||
exists(DataFlow::Node arg |
|
||||
arg.asExpr() = e and
|
||||
arg = call.getArgument(0) and
|
||||
call.getNumArgument() = 1 and
|
||||
FlowSteps::argumentPassing(call, arg, this, sanitizedParameter) and
|
||||
outcome = sanitizerOutcome
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function applies to the flow in `cfg`.
|
||||
*/
|
||||
predicate appliesTo(Configuration cfg) {
|
||||
cfg.isBarrierGuard(sanitizer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that sanitizes an argument.
|
||||
*/
|
||||
private class AdditionalSanitizingCall extends AdditionalSanitizerGuardNode, DataFlow::CallNode {
|
||||
SanitizingFunction f;
|
||||
|
||||
AdditionalSanitizingCall() { f.isSanitizingCall(this, _, _) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { f.isSanitizingCall(this, e, outcome) }
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { f.appliesTo(cfg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An equality test on `e.origin` or `e.source` where `e` is a `postMessage` event object,
|
||||
|
||||
@@ -246,3 +246,14 @@ private class SuperAgentUrlRequest extends CustomClientRequest {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `XMLHttpRequest` browser class.
|
||||
*/
|
||||
private class XMLHttpRequest extends CustomClientRequest {
|
||||
XMLHttpRequest() { this = DataFlow::globalVarRef("XMLHttpRequest").getAnInstantiation() }
|
||||
|
||||
override DataFlow::Node getUrl() { result = getAMethodCall("open").getArgument(1) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getAMethodCall("send").getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -340,3 +340,24 @@ private class JQueryChainedElement extends DOM::Element {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `jQuery.ajax` or `jQuery.getJSON`.
|
||||
*/
|
||||
private class JQueryClientRequest extends CustomClientRequest {
|
||||
JQueryClientRequest() {
|
||||
exists(string name |
|
||||
name = "ajax" or
|
||||
name = "getJSON"
|
||||
|
|
||||
this = jquery().getAMemberCall(name)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = getArgument(0) or
|
||||
result = getOptionArgument([0 .. 1], "url")
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getOptionArgument([0 .. 1], "data") }
|
||||
}
|
||||
|
||||
@@ -38,3 +38,25 @@
|
||||
| tst.js:226:9:226:26 | -1 >= o.indexOf(v) | ExampleConfiguration | false | tst.js:226:25:226:25 | v |
|
||||
| tst.js:236:9:236:24 | isWhitelisted(v) | ExampleConfiguration | true | tst.js:236:23:236:23 | v |
|
||||
| tst.js:240:9:240:28 | config.allowValue(v) | ExampleConfiguration | true | tst.js:240:27:240:27 | v |
|
||||
| tst.js:252:16:252:36 | whiteli ... ains(x) | ExampleConfiguration | true | tst.js:252:35:252:35 | x |
|
||||
| tst.js:254:9:254:12 | f(v) | ExampleConfiguration | true | tst.js:254:11:254:11 | v |
|
||||
| tst.js:261:25:261:45 | whiteli ... ains(y) | ExampleConfiguration | true | tst.js:261:44:261:44 | y |
|
||||
| tst.js:264:9:264:12 | g(v) | ExampleConfiguration | true | tst.js:264:11:264:11 | v |
|
||||
| tst.js:271:25:271:45 | whiteli ... ains(z) | ExampleConfiguration | true | tst.js:271:44:271:44 | z |
|
||||
| tst.js:281:16:281:25 | x2 != null | ExampleConfiguration | false | tst.js:281:16:281:17 | x2 |
|
||||
| tst.js:281:30:281:51 | whiteli ... ins(x2) | ExampleConfiguration | true | tst.js:281:49:281:50 | x2 |
|
||||
| tst.js:283:9:283:13 | f2(v) | ExampleConfiguration | true | tst.js:283:12:283:12 | v |
|
||||
| tst.js:290:16:290:25 | x3 == null | ExampleConfiguration | true | tst.js:290:16:290:17 | x3 |
|
||||
| tst.js:290:30:290:51 | whiteli ... ins(x3) | ExampleConfiguration | true | tst.js:290:49:290:50 | x3 |
|
||||
| tst.js:299:17:299:38 | whiteli ... ins(x4) | ExampleConfiguration | true | tst.js:299:36:299:37 | x4 |
|
||||
| tst.js:308:18:308:39 | whiteli ... ins(x5) | ExampleConfiguration | true | tst.js:308:37:308:38 | x5 |
|
||||
| tst.js:317:26:317:47 | whiteli ... ins(x6) | ExampleConfiguration | true | tst.js:317:45:317:46 | x6 |
|
||||
| tst.js:327:25:327:34 | x7 != null | ExampleConfiguration | false | tst.js:327:25:327:26 | x7 |
|
||||
| tst.js:327:39:327:60 | whiteli ... ins(x7) | ExampleConfiguration | true | tst.js:327:58:327:59 | x7 |
|
||||
| tst.js:330:9:330:13 | f7(v) | ExampleConfiguration | true | tst.js:330:12:330:12 | v |
|
||||
| tst.js:337:25:337:46 | whiteli ... ins(x8) | ExampleConfiguration | true | tst.js:337:44:337:45 | x8 |
|
||||
| tst.js:338:16:338:25 | x8 != null | ExampleConfiguration | false | tst.js:338:16:338:17 | x8 |
|
||||
| tst.js:347:29:347:50 | whiteli ... ins(x9) | ExampleConfiguration | true | tst.js:347:48:347:49 | x9 |
|
||||
| tst.js:356:16:356:27 | x10 !== null | ExampleConfiguration | false | tst.js:356:16:356:18 | x10 |
|
||||
| tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:32:356:34 | x10 |
|
||||
| tst.js:358:9:358:14 | f10(v) | ExampleConfiguration | false | tst.js:358:13:358:13 | v |
|
||||
|
||||
@@ -36,3 +36,23 @@
|
||||
| tst.js:227:14:227:14 | v | tst.js:199:13:199:20 | SOURCE() |
|
||||
| tst.js:239:14:239:14 | v | tst.js:235:13:235:20 | SOURCE() |
|
||||
| tst.js:243:14:243:14 | v | tst.js:235:13:235:20 | SOURCE() |
|
||||
| tst.js:249:10:249:10 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:257:14:257:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:267:14:267:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:275:14:275:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:277:14:277:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:286:14:286:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:293:14:293:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:295:14:295:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:302:14:302:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:304:14:304:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:311:14:311:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:313:14:313:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:321:14:321:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:323:14:323:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:333:14:333:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:341:14:341:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:343:14:343:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:350:14:350:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:352:14:352:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:359:14:359:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
|
||||
@@ -31,3 +31,9 @@
|
||||
| tst.js:229:14:229:14 | v | ExampleConfiguration |
|
||||
| tst.js:237:14:237:14 | v | ExampleConfiguration |
|
||||
| tst.js:241:14:241:14 | v | ExampleConfiguration |
|
||||
| tst.js:255:14:255:14 | v | ExampleConfiguration |
|
||||
| tst.js:265:14:265:14 | v | ExampleConfiguration |
|
||||
| tst.js:284:14:284:14 | v | ExampleConfiguration |
|
||||
| tst.js:331:14:331:14 | v | ExampleConfiguration |
|
||||
| tst.js:356:16:356:27 | x10 | ExampleConfiguration |
|
||||
| tst.js:361:14:361:14 | v | ExampleConfiguration |
|
||||
|
||||
@@ -243,3 +243,122 @@ function adhocWhitelisting() {
|
||||
SINK(v);
|
||||
|
||||
}
|
||||
|
||||
function IndirectSanitizer () {
|
||||
var v = SOURCE();
|
||||
SINK(v);
|
||||
|
||||
function f(x) {
|
||||
return whitelist.contains(x);
|
||||
}
|
||||
if (f(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function g(y) {
|
||||
var sanitized = whitelist.contains(y);
|
||||
return sanitized;
|
||||
}
|
||||
if (g(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function h(z) {
|
||||
var sanitized = whitelist.contains(z);
|
||||
return somethingElse();
|
||||
}
|
||||
if (h(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f2(x2) {
|
||||
return x2 != null && whitelist.contains(x2);
|
||||
}
|
||||
if (f2(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f3(x3) {
|
||||
return x3 == null || whitelist.contains(x3);
|
||||
}
|
||||
if (f3(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f4(x4) {
|
||||
return !whitelist.contains(x4);
|
||||
}
|
||||
if (f4(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
}
|
||||
|
||||
function f5(x5) {
|
||||
return !!whitelist.contains(x5);
|
||||
}
|
||||
if (f5(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f6(x6) {
|
||||
var sanitized = !whitelist.contains(x6);
|
||||
return !sanitized;
|
||||
}
|
||||
if (f6(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
}
|
||||
|
||||
function f7(x7) {
|
||||
var sanitized = x7 != null && whitelist.contains(x7);
|
||||
return sanitized;
|
||||
}
|
||||
if (f7(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f8(x8) {
|
||||
var sanitized = whitelist.contains(x8);
|
||||
return x8 != null && sanitized;
|
||||
}
|
||||
if (f8(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f9(x9) {
|
||||
return unknown() && whitelist.contains(x9) && unknown();
|
||||
}
|
||||
if (f9(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f10(x10) {
|
||||
return x10 !== null || x10 !== undefined;
|
||||
}
|
||||
if (f10(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,3 +27,8 @@
|
||||
| tst.js:67:5:67:24 | superagent.post(url) |
|
||||
| tst.js:68:5:68:23 | superagent.get(url) |
|
||||
| tst.js:69:5:69:23 | superagent.get(url) |
|
||||
| tst.js:74:5:74:29 | $.ajax( ... data}) |
|
||||
| tst.js:75:5:75:35 | $.ajax( ... data}) |
|
||||
| tst.js:77:5:77:32 | $.getJS ... data}) |
|
||||
| tst.js:78:5:78:38 | $.getJS ... data}) |
|
||||
| tst.js:80:15:80:34 | new XMLHttpRequest() |
|
||||
|
||||
@@ -15,3 +15,6 @@
|
||||
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:34:68:43 | headerData |
|
||||
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:52:68:60 | queryData |
|
||||
| tst.js:69:5:69:23 | superagent.get(url) | tst.js:69:48:69:56 | queryData |
|
||||
| tst.js:74:5:74:29 | $.ajax( ... data}) | tst.js:74:24:74:27 | data |
|
||||
| tst.js:77:5:77:32 | $.getJS ... data}) | tst.js:77:27:77:30 | data |
|
||||
| tst.js:80:15:80:34 | new XMLHttpRequest() | tst.js:82:14:82:17 | data |
|
||||
|
||||
@@ -31,3 +31,10 @@
|
||||
| tst.js:67:5:67:24 | superagent.post(url) | tst.js:67:21:67:23 | url |
|
||||
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:20:68:22 | url |
|
||||
| tst.js:69:5:69:23 | superagent.get(url) | tst.js:69:20:69:22 | url |
|
||||
| tst.js:74:5:74:29 | $.ajax( ... data}) | tst.js:74:12:74:14 | url |
|
||||
| tst.js:75:5:75:35 | $.ajax( ... data}) | tst.js:75:12:75:34 | {url: u ... : data} |
|
||||
| tst.js:75:5:75:35 | $.ajax( ... data}) | tst.js:75:18:75:20 | url |
|
||||
| tst.js:77:5:77:32 | $.getJS ... data}) | tst.js:77:15:77:17 | url |
|
||||
| tst.js:78:5:78:38 | $.getJS ... data}) | tst.js:78:15:78:37 | {url: u ... : data} |
|
||||
| tst.js:78:5:78:38 | $.getJS ... data}) | tst.js:78:21:78:23 | url |
|
||||
| tst.js:80:15:80:34 | new XMLHttpRequest() | tst.js:81:17:81:19 | url |
|
||||
|
||||
@@ -69,3 +69,15 @@ import {ClientRequest, net} from 'electron';
|
||||
superagent.get(url).unknown(nonData).query(queryData);
|
||||
|
||||
});
|
||||
|
||||
(function() {
|
||||
$.ajax(url, {data: data});
|
||||
$.ajax({url: url, tdata: data});
|
||||
|
||||
$.getJSON(url, {data: data});
|
||||
$.getJSON({url: url, tdata: data});
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open(_, url);
|
||||
xhr.send(data);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
| real-world-examples.js:5:4:5:11 | o.p = 42 | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:10:2:10:9 | o.p = 42 | another property write |
|
||||
| real-world-examples.js:15:9:15:18 | o.p1 += 42 | This write to property 'p1' is useless, since $@ always overrides it. | real-world-examples.js:15:2:15:18 | o.p1 = o.p1 += 42 | another property write |
|
||||
| real-world-examples.js:16:11:16:20 | o.p2 *= 42 | This write to property 'p2' is useless, since $@ always overrides it. | real-world-examples.js:16:2:16:21 | o.p2 -= (o.p2 *= 42) | another property write |
|
||||
| real-world-examples.js:29:5:29:12 | o.p = 42 | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:32:3:32:10 | o.p = 42 | another property write |
|
||||
| real-world-examples.js:38:15:38:24 | o.p = f3() | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:38:2:38:31 | o.p = f ... : f4() | another property write |
|
||||
| tst.js:3:5:3:16 | o.pure1 = 42 | This write to property 'pure1' is useless, since $@ always overrides it. | tst.js:4:5:4:16 | o.pure1 = 42 | another property write |
|
||||
| tst.js:6:5:6:16 | o.pure2 = 42 | This write to property 'pure2' is useless, since $@ always overrides it. | tst.js:7:5:7:16 | o.pure2 = 43 | another property write |
|
||||
| tst.js:13:5:13:16 | o.pure4 = 42 | This write to property 'pure4' is useless, since $@ always overrides it. | tst.js:15:5:15:16 | o.pure4 = 42 | another property write |
|
||||
| tst.js:20:5:20:17 | o.pure6 = f() | This write to property 'pure6' is useless, since $@ always overrides it. | tst.js:21:5:21:16 | o.pure6 = 42 | another property write |
|
||||
| tst.js:23:5:23:16 | o.pure7 = 42 | This write to property 'pure7' is useless, since $@ always overrides it. | tst.js:25:5:25:16 | o.pure7 = 42 | another property write |
|
||||
| tst.js:76:5:76:34 | o.pure1 ... te = 42 | This write to property 'pure16_simpleAliasWrite' is useless, since $@ always overrides it. | tst.js:77:5:77:36 | o16.pur ... te = 42 | another property write |
|
||||
| tst.js:95:5:95:17 | o.pure18 = 42 | This write to property 'pure18' is useless, since $@ always overrides it. | tst.js:96:5:96:17 | o.pure18 = 42 | another property write |
|
||||
| tst.js:96:5:96:17 | o.pure18 = 42 | This write to property 'pure18' is useless, since $@ always overrides it. | tst.js:97:5:97:17 | o.pure18 = 42 | another property write |
|
||||
@@ -0,0 +1 @@
|
||||
Declarations/DeadStoreOfProperty.ql
|
||||
@@ -0,0 +1,12 @@
|
||||
(function(){
|
||||
var o = {};
|
||||
o.prop1 = o['prop1'] = x;
|
||||
|
||||
o['prop2'] = o.prop2 = x;
|
||||
|
||||
o.prop3 = x
|
||||
o['prop3'] = x;
|
||||
|
||||
o['prop4'] = x;
|
||||
o.prop4 = x
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
// Adapted from the Google Closure externs; original copyright header included below.
|
||||
/*
|
||||
* Copyright 2008 The Closure Compiler Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
function EventTarget() {}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements {EventTarget}
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247
|
||||
*/
|
||||
function Node() {}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {Node}
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-745549614
|
||||
*/
|
||||
function Element() {}
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @see http://www.w3.org/TR/cssom-view/#dom-element-clienttop
|
||||
*/
|
||||
Element.prototype.clientTop;
|
||||
|
||||
//semmle-extractor-options: --externs
|
||||
@@ -0,0 +1,13 @@
|
||||
(function(){
|
||||
var first = f();
|
||||
|
||||
function flush() {
|
||||
var flushed = first;
|
||||
|
||||
var next = g();
|
||||
first = next;
|
||||
next.prev = 42;
|
||||
|
||||
flushed.prev = 42;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
(function(){
|
||||
var o = f1();
|
||||
while (f2()) {
|
||||
if (f4()) {
|
||||
o.p = 42; // NOT OK
|
||||
break;
|
||||
}
|
||||
f5();
|
||||
}
|
||||
o.p = 42;
|
||||
});
|
||||
|
||||
(function(){
|
||||
var o = f1();
|
||||
o.p1 = o.p1 += 42; // NOT OK
|
||||
o.p2 -= (o.p2 *= 42); // NOT OK
|
||||
});
|
||||
|
||||
(function(){
|
||||
var o = f1();
|
||||
o.m(function () {
|
||||
if (f2()) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
f3();
|
||||
} catch (e) {
|
||||
f4();
|
||||
o.p = 42; // NOT OK
|
||||
}
|
||||
}
|
||||
o.p = 42;
|
||||
});
|
||||
});
|
||||
|
||||
(function(){
|
||||
var o = f1();
|
||||
o.p = f2() ? o.p = f3() : f4(); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
(function(){
|
||||
var o = {};
|
||||
o.pure1 = 42; // NOT OK
|
||||
o.pure1 = 42;
|
||||
|
||||
o.pure2 = 42; // NOT OK
|
||||
o.pure2 = 43;
|
||||
|
||||
o.impure3 = 42;
|
||||
f();
|
||||
o.impure3 = 42;
|
||||
|
||||
o.pure4 = 42; // NOT OK
|
||||
43;
|
||||
o.pure4 = 42;
|
||||
|
||||
o.impure5 = 42;
|
||||
o.impure5 = f();
|
||||
|
||||
o.pure6 = f(); // NOT OK
|
||||
o.pure6 = 42;
|
||||
|
||||
o.pure7 = 42; // NOT OK
|
||||
if(x){}
|
||||
o.pure7 = 42;
|
||||
|
||||
o.pure8_cond = 42;
|
||||
if(x){
|
||||
o.pure8_cond = 42;
|
||||
}
|
||||
|
||||
o.impure9 = 42;
|
||||
f();
|
||||
if(x){
|
||||
}
|
||||
o.impure9 = 42;
|
||||
|
||||
o.impure10 = 42;
|
||||
if(x){
|
||||
f();
|
||||
}
|
||||
o.impure10 = 42;
|
||||
|
||||
o.impure11 = 42;
|
||||
if(x){
|
||||
|
||||
}
|
||||
f();
|
||||
o.impure11 = 42;
|
||||
|
||||
o.pure12_read = 42;
|
||||
o.pure12_read;
|
||||
o.pure12_read = 42;
|
||||
|
||||
var o2;
|
||||
o.pure13_otherRead = 42;
|
||||
o2.pure13_otherRead;
|
||||
o.pure13_otherRead = 42;
|
||||
|
||||
function id14(e) {
|
||||
return e;
|
||||
}
|
||||
var o14 = id14(o);
|
||||
o.pure14_aliasRead = 42;
|
||||
o14.pure14_aliasRead;
|
||||
o.pure14_aliasRead = 42;
|
||||
|
||||
function id15(e) {
|
||||
return e;
|
||||
}
|
||||
var o15 = id15(o);
|
||||
o.pure15_aliasWrite = 42;
|
||||
o15.pure15_aliasWrite = 42;
|
||||
|
||||
var o16 = x? o: null;
|
||||
o.pure16_simpleAliasWrite = 42; // NOT OK
|
||||
o16.pure16_simpleAliasWrite = 42;
|
||||
|
||||
var o17 = {
|
||||
duplicate17: 42,
|
||||
duplicate17: 42
|
||||
}
|
||||
|
||||
// DOM
|
||||
o.clientTop = 42;
|
||||
o.clientTop = 42;
|
||||
|
||||
o.defaulted1 = null;
|
||||
o.defaulted1 = 42;
|
||||
|
||||
o.defaulted2 = -1;
|
||||
o.defaulted2 = 42;
|
||||
|
||||
var o = {};
|
||||
o.pure18 = 42; // NOT OK
|
||||
o.pure18 = 42; // NOT OK
|
||||
o.pure18 = 42;
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
| tst.js:162:9:162:16 | typeof x | This expression is of type string, but it is compared to $@ of type undefined. | tst.js:162:22:162:30 | undefined | 'undefined' |
|
||||
| tst.js:163:9:163:21 | typeof window | This expression is of type string, but it is compared to $@ of type undefined. | tst.js:163:27:163:35 | undefined | 'undefined' |
|
||||
| tst.js:165:9:165:16 | typeof x | This expression is of type string, but it is compared to $@ of type undefined. | tst.js:165:22:165:22 | u | variable 'u' |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/HeterogeneousComparison.ql
|
||||
@@ -0,0 +1,72 @@
|
||||
| module-environment-detection.js:23:8:23:36 | 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:19:5:19:5 | n | This guard always evaluates to false. |
|
||||
| tst.js:20:5:20:5 | o | This guard always evaluates to true. |
|
||||
| tst.js:23:5:23:5 | u | This guard always evaluates to false. |
|
||||
| tst.js:24:5:24:5 | n | This guard always evaluates to false. |
|
||||
| tst.js:25:5:25:5 | o | This guard always evaluates to true. |
|
||||
| tst.js:28:5:28:6 | !u | This guard always evaluates to true. |
|
||||
| tst.js:29:5:29:6 | !n | This guard always evaluates to true. |
|
||||
| tst.js:30:5:30:6 | !o | This guard always evaluates to false. |
|
||||
| tst.js:33:5:33:7 | !!u | This guard always evaluates to false. |
|
||||
| tst.js:34:5:34:7 | !!n | This guard always evaluates to false. |
|
||||
| tst.js:35:5:35:7 | !!o | This guard always evaluates to true. |
|
||||
| tst.js:38:5:38:18 | u != undefined | This guard always evaluates to false. |
|
||||
| tst.js:39:5:39:18 | n != undefined | This guard always evaluates to false. |
|
||||
| tst.js:40:5:40:18 | o != undefined | This guard always evaluates to true. |
|
||||
| tst.js:43:5:43:18 | u == undefined | This guard always evaluates to true. |
|
||||
| tst.js:44:5:44:18 | n == undefined | This guard always evaluates to true. |
|
||||
| tst.js:45:5:45:18 | o == undefined | This guard always evaluates to false. |
|
||||
| tst.js:48:5:48:19 | u === undefined | This guard always evaluates to true. |
|
||||
| tst.js:49:5:49:19 | n === undefined | This guard always evaluates to false. |
|
||||
| tst.js:50:5:50:19 | o === undefined | This guard always evaluates to false. |
|
||||
| tst.js:53:9:53:9 | u | This guard always evaluates to false. |
|
||||
| tst.js:56:9:56:9 | n | This guard always evaluates to false. |
|
||||
| tst.js:59:9:59:9 | o | This guard always evaluates to true. |
|
||||
| tst.js:66:5:66:5 | u | This guard always evaluates to false. |
|
||||
| tst.js:67:5:67:5 | n | This guard always evaluates to false. |
|
||||
| tst.js:68:5:68:5 | o | This guard always evaluates to true. |
|
||||
| tst.js:71:9:71:23 | u !== undefined | This guard always evaluates to false. |
|
||||
| tst.js:74:9:74:23 | n !== undefined | This guard always evaluates to true. |
|
||||
| tst.js:77:9:77:23 | o !== undefined | This guard always evaluates to true. |
|
||||
| tst.js:84:9:84:22 | u == undefined | This guard always evaluates to true. |
|
||||
| tst.js:85:9:85:22 | n == undefined | This guard always evaluates to true. |
|
||||
| tst.js:86:9:86:22 | o == undefined | This guard always evaluates to false. |
|
||||
| tst.js:89:9:89:22 | u != undefined | This guard always evaluates to false. |
|
||||
| tst.js:90:9:90:22 | n != undefined | This guard always evaluates to false. |
|
||||
| tst.js:91:9:91:22 | o != undefined | This guard always evaluates to true. |
|
||||
| tst.js:94:9:94:32 | typeof ... efined" | This guard always evaluates to true. |
|
||||
| tst.js:95:9:95:32 | typeof ... efined" | This guard always evaluates to false. |
|
||||
| tst.js:96:9:96:32 | typeof ... efined" | This guard always evaluates to false. |
|
||||
| tst.js:100:5:100:27 | typeof ... nction" | This guard always evaluates to true. |
|
||||
| tst.js:101:5:101:27 | typeof ... nction" | This guard always evaluates to false. |
|
||||
| tst.js:114:5:114:15 | empty_array | This guard always evaluates to true. |
|
||||
| tst.js:115:5:115:22 | pseudo_empty_array | This guard always evaluates to true. |
|
||||
| tst.js:116:5:116:19 | non_empty_array | This guard always evaluates to true. |
|
||||
| tst.js:124:6:124:20 | u !== undefined | This guard always evaluates to false. |
|
||||
| tst.js:124:25:124:34 | u !== null | This guard always evaluates to true. |
|
||||
| tst.js:125:5:125:19 | u !== undefined | This guard always evaluates to false. |
|
||||
| tst.js:125:24:125:33 | u !== null | This guard always evaluates to true. |
|
||||
| tst.js:127:5:127:18 | u != undefined | This guard always evaluates to false. |
|
||||
| tst.js:127:23:127:31 | u != null | This guard always evaluates to false. |
|
||||
| tst.js:127:23:127:31 | u != null | This guard always evaluates to true. |
|
||||
| tst.js:128:5:128:18 | u == undefined | This guard always evaluates to true. |
|
||||
| tst.js:128:23:128:31 | u == null | This guard always evaluates to false. |
|
||||
| tst.js:128:23:128:31 | u == null | This guard always evaluates to true. |
|
||||
| tst.js:129:5:129:19 | u !== undefined | This guard always evaluates to false. |
|
||||
| tst.js:129:24:129:33 | u !== null | This guard always evaluates to true. |
|
||||
| tst.js:130:5:130:22 | !(u === undefined) | This guard always evaluates to false. |
|
||||
| tst.js:130:27:130:39 | !(u === null) | This guard always evaluates to true. |
|
||||
| tst.js:131:5:131:19 | u === undefined | This guard always evaluates to true. |
|
||||
| tst.js:131:24:131:33 | u === null | This guard always evaluates to false. |
|
||||
| tst.js:132:7:132:21 | u === undefined | This guard always evaluates to true. |
|
||||
| tst.js:132:26:132:35 | u === null | This guard always evaluates to false. |
|
||||
| tst.js:133:5:133:22 | !(u === undefined) | This guard always evaluates to false. |
|
||||
| tst.js:133:27:133:36 | u !== null | This guard always evaluates to true. |
|
||||
| tst.js:135:5:135:18 | u == undefined | This guard always evaluates to true. |
|
||||
| tst.js:135:23:135:31 | u == null | This guard always evaluates to true. |
|
||||
| tst.js:138:24:138:33 | x === null | This guard always evaluates to false. |
|
||||
| tst.js:140:13:140:22 | x === null | This guard always evaluates to false. |
|
||||
| tst.js:156:23:156:31 | x != null | This guard always evaluates to true. |
|
||||
| tst.js:158:13:158:21 | x != null | This guard always evaluates to true. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/UnneededDefensiveProgramming.ql
|
||||
@@ -0,0 +1 @@
|
||||
Statements/UselessConditional.ql
|
||||
@@ -0,0 +1,11 @@
|
||||
var Mod1;
|
||||
(function (Mod1) {
|
||||
Mod1.p = 42;
|
||||
})(Mod1 || (Mod1 = {}));
|
||||
|
||||
(function(){
|
||||
var Mod2;
|
||||
(function (Mod2) {
|
||||
Mod2.p = 42;
|
||||
})(Mod2 || (Mod2 = {})); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
var _ = (function() {
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = _;
|
||||
}
|
||||
exports._ = _;
|
||||
}
|
||||
return {
|
||||
define: function(name, factory) {
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = emmet;
|
||||
}
|
||||
exports.emmet = emmet;
|
||||
}
|
||||
|
||||
(function(){
|
||||
var module;
|
||||
if(typeof module === 'undefined'); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,174 @@
|
||||
(function(){
|
||||
var v;
|
||||
var u = undefined;
|
||||
var n = null;
|
||||
var o = {};
|
||||
var x = functionWithUnknownReturnValue();
|
||||
|
||||
var u_ = u;
|
||||
var n_ = n;
|
||||
var o_ = o;
|
||||
var x_ = x;
|
||||
|
||||
u_ = u_ || e; // NOT OK
|
||||
n_ = n_ || e; // NOT OK
|
||||
o_ = o_ || e; // NOT OK
|
||||
x_ = x_ || e;
|
||||
|
||||
u && u.p; // NOT OK
|
||||
n && n.p; // NOT OK
|
||||
o && o.p; // NOT OK
|
||||
x && x.p;
|
||||
|
||||
u && u(); // NOT OK
|
||||
n && n(); // NOT OK
|
||||
o && o(); // NOT OK
|
||||
x && x();
|
||||
|
||||
!u || u.p; // NOT OK
|
||||
!n || n.p; // NOT OK
|
||||
!o || o.p; // NOT OK
|
||||
!x || x.p;
|
||||
|
||||
!!u && u.p; // NOT OK
|
||||
!!n && n.p; // NOT OK
|
||||
!!o && o.p; // NOT OK
|
||||
!!x && x.p;
|
||||
|
||||
u != undefined && u.p; // NOT OK
|
||||
n != undefined && n.p; // NOT OK
|
||||
o != undefined && o.p; // NOT OK
|
||||
x != undefined && x.p;
|
||||
|
||||
u == undefined || u.p; // NOT OK
|
||||
n == undefined || n.p; // NOT OK
|
||||
o == undefined || o.p; // NOT OK
|
||||
x == undefined || x.p;
|
||||
|
||||
u === undefined || u.p; // NOT OK
|
||||
n === undefined || n.p; // NOT OK
|
||||
o === undefined || o.p; // NOT OK
|
||||
x === undefined || x.p;
|
||||
|
||||
if (u) { // NOT OK
|
||||
u.p;
|
||||
}
|
||||
if (n) { // NOT OK
|
||||
n.p;
|
||||
}
|
||||
if (o) { // NOT OK
|
||||
o.p;
|
||||
}
|
||||
if (x) {
|
||||
x.p;
|
||||
}
|
||||
|
||||
u? u():_; // NOT OK
|
||||
n? n(): _; // NOT OK
|
||||
o? o(): _; // NOT OK
|
||||
x? x(): _;
|
||||
|
||||
if (u !== undefined) { // NOT OK
|
||||
u.p;
|
||||
}
|
||||
if (n !== undefined) { // NOT OK
|
||||
n.p;
|
||||
}
|
||||
if (o !== undefined) { // NOT OK
|
||||
o.p;
|
||||
}
|
||||
if (x !== undefined) {
|
||||
x.p;
|
||||
}
|
||||
|
||||
if (u == undefined){} // NOT OK
|
||||
if (n == undefined){} // NOT OK
|
||||
if (o == undefined){} // NOT OK
|
||||
if (x == undefined){}
|
||||
|
||||
if (u != undefined){} // NOT OK
|
||||
if (n != undefined){} // NOT OK
|
||||
if (o != undefined){} // NOT OK
|
||||
if (x != undefined){}
|
||||
|
||||
if (typeof u === "undefined"){} // NOT OK
|
||||
if (typeof n === "undefined"){} // NOT OK
|
||||
if (typeof o === "undefined"){} // NOT OK
|
||||
if (typeof x === "undefined"){}
|
||||
|
||||
function f() { }
|
||||
typeof f === "function" && f(); // NOT OK
|
||||
typeof u === "function" && u(); // NOT OK
|
||||
typeof x === "function" && x();
|
||||
|
||||
var empty_array = [];
|
||||
var pseudo_empty_array = [''];
|
||||
var non_empty_array = ['foo'];
|
||||
var empty_string = "";
|
||||
var non_empty_string = "foo";
|
||||
var zero = 0;
|
||||
var neg = -1;
|
||||
var _true = true;
|
||||
var _false = false;
|
||||
|
||||
empty_array && empty_array.pop(); // NOT OK
|
||||
pseudo_empty_array && pseudo_empty_array.pop(); // NOT OK
|
||||
non_empty_array && non_empty_array.pop(); // NOT OK
|
||||
empty_string && empty_string.charAt(0);
|
||||
non_empty_string && non_empty_string.charAt(0);
|
||||
zero && zero();
|
||||
neg && neg();
|
||||
_true && _true();
|
||||
_false && _false();
|
||||
|
||||
(u !== undefined && u !== null) && u.p; // NOT OK
|
||||
u !== undefined && u !== null && u.p; // NOT OK
|
||||
|
||||
u != undefined && u != null; // NOT OK
|
||||
u == undefined || u == null; // NOT OK
|
||||
u !== undefined && u !== null; // NOT OK
|
||||
!(u === undefined) && !(u === null); // NOT OK
|
||||
u === undefined || u === null; // NOT OK
|
||||
!(u === undefined || u === null); // NOT OK
|
||||
!(u === undefined) && u !== null; // NOT OK
|
||||
u !== undefined && n !== null;
|
||||
u == undefined && u == null; // NOT OK
|
||||
x == undefined && x == null;
|
||||
|
||||
x === undefined && x === null; // NOT OK
|
||||
if (x === undefined) {
|
||||
if (x === null) { // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
x !== undefined && x !== null;
|
||||
if (x !== undefined) {
|
||||
if (x !== null) {
|
||||
}
|
||||
}
|
||||
|
||||
x == undefined && x == null;
|
||||
if (x == undefined) {
|
||||
if (x == null) {
|
||||
}
|
||||
}
|
||||
|
||||
x != undefined && x != null; // NOT OK
|
||||
if (x != undefined) {
|
||||
if (x != null) { // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof x !== undefined);
|
||||
if (typeof window !== undefined);
|
||||
if (typeof x !== x);
|
||||
if (typeof x !== u); // NOT OK
|
||||
|
||||
if (typeof window !== "undefined");
|
||||
if (typeof module !== "undefined");
|
||||
if (typeof global !== "undefined");
|
||||
|
||||
if (typeof window !== "undefined" && window.document);
|
||||
if (typeof module !== "undefined" && module.exports);
|
||||
if (typeof global !== "undefined" && global.process);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
(function(){
|
||||
var v;
|
||||
(function(){
|
||||
if(typeof v === "undefined"){ // NOT OK
|
||||
v = 42;
|
||||
}
|
||||
for(var v in x){
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2,12 +2,12 @@
|
||||
| express.js:12:26:12:44 | req.param("target") | Untrusted URL redirection due to $@. | express.js:12:26:12:44 | req.param("target") | user-provided value |
|
||||
| express.js:33:18:33:23 | target | Untrusted URL redirection due to $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
|
||||
| express.js:35:16:35:21 | target | Untrusted URL redirection due to $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
|
||||
| express.js:44:16:44:108 | (req.pa ... ntacts" | Untrusted URL redirection due to $@. | express.js:44:69:44:87 | req.param('action') | user-provided value |
|
||||
| express.js:53:26:53:28 | url | Untrusted URL redirection due to $@. | express.js:48:16:48:28 | req.params[0] | user-provided value |
|
||||
| express.js:78:16:78:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:78:19:78:37 | req.param("target") | user-provided value |
|
||||
| express.js:94:18:94:23 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:101:16:101:21 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:122:16:122:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:122:17:122:30 | req.query.page | user-provided value |
|
||||
| express.js:40:16:40:108 | (req.pa ... ntacts" | Untrusted URL redirection due to $@. | express.js:40:69:40:87 | req.param('action') | user-provided value |
|
||||
| express.js:49:26:49:28 | url | Untrusted URL redirection due to $@. | express.js:44:16:44:28 | req.params[0] | user-provided value |
|
||||
| express.js:74:16:74:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:74:19:74:37 | req.param("target") | user-provided value |
|
||||
| express.js:90:18:90:23 | target | Untrusted URL redirection due to $@. | express.js:83:16:83:34 | req.param("target") | user-provided value |
|
||||
| express.js:97:16:97:21 | target | Untrusted URL redirection due to $@. | express.js:83:16:83:34 | req.param("target") | user-provided value |
|
||||
| express.js:118:16:118:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:118:17:118:30 | req.query.page | user-provided value |
|
||||
| node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
|
||||
| node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
|
||||
| node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |
|
||||
|
||||
@@ -35,10 +35,6 @@ app.get('/some/path', function(req, res) {
|
||||
res.redirect(target);
|
||||
});
|
||||
|
||||
function isLocalURL(target) {
|
||||
return new RegExp("^/(?![/\\])|^~/").exec(target);
|
||||
}
|
||||
|
||||
app.get('/foo', function(req, res) {
|
||||
// BAD: may be a global redirection
|
||||
res.redirect((req.param('action') && req.param('action') != "") ? req.param('action') : "/google_contacts")
|
||||
|
||||
Reference in New Issue
Block a user