mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #2251 from asger-semmle/barrier-guard-improvements
Approved by esbena
This commit is contained in:
@@ -147,7 +147,7 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
exists(BarrierGuardNode guard |
|
||||
isBarrierGuard(guard) and
|
||||
isBarrierGuardInternal(guard) and
|
||||
guard.internalBlocks(node, "")
|
||||
)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
predicate isLabeledBarrier(DataFlow::Node node, FlowLabel lbl) {
|
||||
exists(BarrierGuardNode guard |
|
||||
isBarrierGuard(guard) and
|
||||
isBarrierGuardInternal(guard) and
|
||||
guard.internalBlocks(node, lbl)
|
||||
)
|
||||
or
|
||||
@@ -198,6 +198,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
predicate isBarrierGuard(BarrierGuardNode guard) { none() }
|
||||
|
||||
private predicate isBarrierGuardInternal(BarrierGuardNode guard) {
|
||||
isBarrierGuard(guard)
|
||||
or
|
||||
guard.(AdditionalBarrierGuardNode).appliesTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -302,42 +308,27 @@ abstract class BarrierGuardNode extends DataFlow::Node {
|
||||
exists(SsaRefinementNode ref, boolean outcome |
|
||||
nd = DataFlow::ssaDefinitionNode(ref) and
|
||||
forex(SsaVariable input | input = ref.getAnInput() |
|
||||
asExpr() = ref.getGuard().getTest() and
|
||||
getExpr() = ref.getGuard().getTest() and
|
||||
outcome = ref.getGuard().(ConditionGuardNode).getOutcome() and
|
||||
internalBlocksExpr(outcome, input.getAUse(), label)
|
||||
barrierGuardBlocksExpr(this, outcome, input.getAUse(), label)
|
||||
)
|
||||
)
|
||||
or
|
||||
// 2) `nd` is an instance of an access path `p`, and dominated by a barrier for `p`
|
||||
exists(AccessPath p, BasicBlock bb, ConditionGuardNode cond, boolean outcome |
|
||||
nd = DataFlow::valueNode(p.getAnInstanceIn(bb)) and
|
||||
asExpr() = cond.getTest() and
|
||||
getExpr() = cond.getTest() and
|
||||
outcome = cond.getOutcome() and
|
||||
internalBlocksAccessPath(outcome, p, label) and
|
||||
barrierGuardBlocksAccessPath(this, outcome, p, label) and
|
||||
cond.dominates(bb)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `nd` acts as a barrier for data flow.
|
||||
*
|
||||
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
|
||||
*/
|
||||
private predicate internalBlocksExpr(boolean outcome, Expr test, string label) {
|
||||
blocks(outcome, test) and label = ""
|
||||
/** Gets the corresponding expression, including that of reflective calls. */
|
||||
private Expr getExpr() {
|
||||
result = asExpr()
|
||||
or
|
||||
blocks(outcome, test, label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
|
||||
* an access path.
|
||||
*
|
||||
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate internalBlocksAccessPath(boolean outcome, AccessPath ap, string label) {
|
||||
internalBlocksExpr(outcome, ap.getAnInstance(), label)
|
||||
this = DataFlow::reflectiveCallNode(result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,6 +344,32 @@ abstract class BarrierGuardNode extends DataFlow::Node {
|
||||
predicate blocks(boolean outcome, Expr e, FlowLabel label) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `nd` acts as a barrier for data flow.
|
||||
*
|
||||
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
|
||||
*/
|
||||
private predicate barrierGuardBlocksExpr(BarrierGuardNode guard, boolean outcome, Expr test, string label) {
|
||||
guard.blocks(outcome, test) and label = ""
|
||||
or
|
||||
guard.blocks(outcome, test, label)
|
||||
or
|
||||
// Handle labelled barrier guard functions specially, to avoid negative recursion
|
||||
// through the non-abstract 3-argument version of blocks().
|
||||
guard.(AdditionalBarrierGuardCall).internalBlocksLabel(outcome, test, label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
|
||||
* an access path.
|
||||
*
|
||||
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate barrierGuardBlocksAccessPath(BarrierGuardNode guard, boolean outcome, AccessPath ap, string label) {
|
||||
barrierGuardBlocksExpr(guard, outcome, ap.getAnInstance(), label)
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard node that only blocks specific labels.
|
||||
*/
|
||||
@@ -1186,3 +1203,110 @@ module PathGraph {
|
||||
not pred = finalMidNode(succ)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets an operand of the given `&&` operator.
|
||||
*
|
||||
* We use this to construct the transitive closure over a relation
|
||||
* that does not include all of `BinaryExpr.getAnOperand`.
|
||||
*/
|
||||
private Expr getALogicalAndOperand(LogAndExpr e) {
|
||||
result = e.getAnOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operand of the given `||` operator.
|
||||
*
|
||||
* We use this to construct the transitive closure over a relation
|
||||
* that does not include all of `BinaryExpr.getAnOperand`.
|
||||
*/
|
||||
private Expr getALogicalOrOperand(LogOrExpr e) {
|
||||
result = e.getAnOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `BarrierGuardNode` that controls which data flow
|
||||
* configurations it is used in.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isBarrierGuard`
|
||||
* for analysis-specific barrier guards.
|
||||
*/
|
||||
abstract class AdditionalBarrierGuardNode extends BarrierGuardNode {
|
||||
abstract predicate appliesTo(Configuration cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that returns the result of a barrier guard.
|
||||
*/
|
||||
private class BarrierGuardFunction extends Function {
|
||||
DataFlow::ParameterNode sanitizedParameter;
|
||||
BarrierGuardNode guard;
|
||||
boolean guardOutcome;
|
||||
string label;
|
||||
|
||||
BarrierGuardFunction() {
|
||||
exists(Expr e |
|
||||
exists(Expr returnExpr |
|
||||
returnExpr = guard.asExpr()
|
||||
or
|
||||
// ad hoc support for conjunctions:
|
||||
getALogicalAndOperand+(returnExpr) = guard.asExpr() and guardOutcome = true
|
||||
or
|
||||
// ad hoc support for disjunctions:
|
||||
getALogicalOrOperand+(returnExpr) = guard.asExpr() and guardOutcome = false
|
||||
|
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.getDef().getSource() = returnExpr and
|
||||
ssa.getVariable().getAUse() = getAReturnedExpr()
|
||||
)
|
||||
or
|
||||
returnExpr = getAReturnedExpr()
|
||||
) and
|
||||
sanitizedParameter.flowsToExpr(e) and
|
||||
barrierGuardBlocksExpr(guard, guardOutcome, e, label)
|
||||
) and
|
||||
getNumParameter() = 1 and
|
||||
sanitizedParameter.getParameter() = getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
|
||||
*/
|
||||
predicate isBarrierCall(DataFlow::CallNode call, Expr e, boolean outcome, string lbl) {
|
||||
exists(DataFlow::Node arg |
|
||||
arg.asExpr() = e and
|
||||
arg = call.getArgument(0) and
|
||||
call.getNumArgument() = 1 and
|
||||
argumentPassing(call, arg, this, sanitizedParameter) and
|
||||
outcome = guardOutcome and
|
||||
lbl = label
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function applies to the flow in `cfg`.
|
||||
*/
|
||||
predicate appliesTo(Configuration cfg) { cfg.isBarrierGuard(guard) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that sanitizes an argument.
|
||||
*/
|
||||
private class AdditionalBarrierGuardCall extends AdditionalBarrierGuardNode, DataFlow::CallNode {
|
||||
BarrierGuardFunction f;
|
||||
|
||||
AdditionalBarrierGuardCall() { f.isBarrierCall(this, _, _, _) }
|
||||
|
||||
override predicate blocks(boolean outcome, Expr e) {
|
||||
f.isBarrierCall(this, e, outcome, "")
|
||||
}
|
||||
|
||||
predicate internalBlocksLabel(boolean outcome, Expr e, DataFlow::FlowLabel label) {
|
||||
f.isBarrierCall(this, e, outcome, label)
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { f.appliesTo(cfg) }
|
||||
}
|
||||
|
||||
@@ -947,6 +947,15 @@ module DataFlow {
|
||||
*/
|
||||
DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot }
|
||||
|
||||
/**
|
||||
* Gets a data flow node representing the underlying call performed by the given
|
||||
* call to `Function.prototype.call` or `Function.prototype.apply`.
|
||||
*
|
||||
* For example, for an expression `fn.call(x, y)`, this gets a call node with `fn` as the
|
||||
* callee, `x` as the receiver, and `y` as the first argument.
|
||||
*/
|
||||
DataFlow::InvokeNode reflectiveCallNode(InvokeExpr expr) { result = TReflectiveCallNode(expr, _) }
|
||||
|
||||
/**
|
||||
* Provides classes representing various kinds of calls.
|
||||
*
|
||||
|
||||
@@ -133,8 +133,8 @@ module TaintTracking {
|
||||
* configurations it is used in.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isSanitizer`
|
||||
* for analysis-specific taint steps.
|
||||
* of the standard library. Override `Configuration::isSanitizerGuard`
|
||||
* for analysis-specific taint sanitizer guards.
|
||||
*/
|
||||
abstract class AdditionalSanitizerGuardNode extends SanitizerGuardNode {
|
||||
/**
|
||||
@@ -846,71 +846,6 @@ module TaintTracking {
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that returns the result of a sanitizer check.
|
||||
*/
|
||||
private class SanitizingFunction extends Function {
|
||||
DataFlow::ParameterNode 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
|
||||
sanitizedParameter.flowsToExpr(e) and
|
||||
sanitizer.sanitizes(sanitizerOutcome, e)
|
||||
) and
|
||||
getNumParameter() = 1 and
|
||||
sanitizedParameter.getParameter() = 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,
|
||||
* considered as a sanitizer for `e`.
|
||||
|
||||
@@ -44,14 +44,11 @@
|
||||
| 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:16:281:25 | x2 != null | ExampleConfiguration | false | tst.js:281:22:281:25 | null |
|
||||
| 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:16:290:25 | x3 == null | ExampleConfiguration | true | tst.js:290:22:290:25 | null |
|
||||
| tst.js:290:30:290:51 | whiteli ... ins(x3) | ExampleConfiguration | true | tst.js:290:49:290:50 | x3 |
|
||||
@@ -61,7 +58,6 @@
|
||||
| tst.js:327:25:327:34 | x7 != null | ExampleConfiguration | false | tst.js:327:25:327:26 | x7 |
|
||||
| tst.js:327:25:327:34 | x7 != null | ExampleConfiguration | false | tst.js:327:31:327:34 | null |
|
||||
| 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:338:16:338:25 | x8 != null | ExampleConfiguration | false | tst.js:338:22:338:25 | null |
|
||||
@@ -70,6 +66,5 @@
|
||||
| tst.js:356:16:356:27 | x10 !== null | ExampleConfiguration | false | tst.js:356:24:356:27 | null |
|
||||
| tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:32:356:34 | x10 |
|
||||
| tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:40:356:48 | undefined |
|
||||
| tst.js:358:9:358:14 | f10(v) | ExampleConfiguration | false | tst.js:358:13:358:13 | v |
|
||||
| tst.js:370:9:370:29 | o.p == ... listed" | ExampleConfiguration | true | tst.js:370:9:370:11 | o.p |
|
||||
| tst.js:377:11:377:32 | o[p] == ... listed" | ExampleConfiguration | true | tst.js:377:11:377:14 | o[p] |
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
| 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() |
|
||||
| tst.js:368:10:368:12 | o.p | tst.js:367:13:367:20 | SOURCE() |
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
| 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:350:14:350:14 | v | ExampleConfiguration |
|
||||
| tst.js:356:16:356:27 | x10 | ExampleConfiguration |
|
||||
| tst.js:356:32:356:34 | x10 | ExampleConfiguration |
|
||||
| tst.js:361:14:361:14 | v | ExampleConfiguration |
|
||||
|
||||
@@ -347,7 +347,7 @@ function IndirectSanitizer () {
|
||||
return unknown() && whitelist.contains(x9) && unknown();
|
||||
}
|
||||
if (f9(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
@@ -69,10 +69,14 @@ typeInferenceMismatch
|
||||
| promise.js:5:25:5:32 | source() | promise.js:5:8:5:33 | bluebir ... urce()) |
|
||||
| promise.js:10:24:10:31 | source() | promise.js:10:8:10:32 | Promise ... urce()) |
|
||||
| promise.js:12:20:12:27 | source() | promise.js:13:8:13:23 | resolver.promise |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:14:10:14:14 | taint |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:33:14:33:18 | taint |
|
||||
| sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:15:10:15:15 | this.x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:21:14:21:19 | this.x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:26:9:26:14 | this.x |
|
||||
| sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:45:8:45:8 | x |
|
||||
| sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:48:10:48:10 | x |
|
||||
| spread.js:2:15:2:22 | source() | spread.js:4:8:4:19 | { ...taint } |
|
||||
| spread.js:2:15:2:22 | source() | spread.js:5:8:5:43 | { f: 'h ... orld' } |
|
||||
| spread.js:2:15:2:22 | source() | spread.js:7:8:7:19 | [ ...taint ] |
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
DataFlow::CallNode getACall(string name) { result.getCalleeName() = name }
|
||||
DataFlow::CallNode getACall(string name) {
|
||||
result.getCalleeName() = name
|
||||
or
|
||||
result.getCalleeNode().getALocalSource() = DataFlow::globalVarRef(name)
|
||||
}
|
||||
|
||||
class Sink extends DataFlow::Node {
|
||||
Sink() { this = getACall("sink").getAnArgument() }
|
||||
|
||||
@@ -41,10 +41,18 @@
|
||||
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:30:14:30:20 | x.value |
|
||||
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:41:10:41:18 | id(taint) |
|
||||
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:51:14:51:14 | x |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:14:10:14:14 | taint |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:17:14:17:18 | taint |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:21:14:21:18 | taint |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:25:14:25:18 | taint |
|
||||
| sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:33:14:33:18 | taint |
|
||||
| sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:15:10:15:15 | this.x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:21:14:21:19 | this.x |
|
||||
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:26:9:26:14 | this.x |
|
||||
| sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:45:8:45:8 | x |
|
||||
| sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:48:10:48:10 | x |
|
||||
| sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:52:10:52:10 | x |
|
||||
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field |
|
||||
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
function test() {
|
||||
function myCheck1(x) {
|
||||
return x === "a" && something() && somethingElse();
|
||||
}
|
||||
function myCheck2(x) {
|
||||
return something() && x === "a" && somethingElse();
|
||||
}
|
||||
function myCheck3(x) {
|
||||
return something() && somethingElse() && x === "a";
|
||||
}
|
||||
|
||||
let taint = source();
|
||||
|
||||
sink(taint); // NOT OK
|
||||
|
||||
if (myCheck1(taint)) {
|
||||
sink(taint); // OK
|
||||
}
|
||||
|
||||
if (myCheck2(taint)) {
|
||||
sink(taint); // OK
|
||||
}
|
||||
|
||||
if (myCheck3(taint)) {
|
||||
sink(taint); // OK
|
||||
}
|
||||
|
||||
function badCheck(x) {
|
||||
return something && x + isSafe(x) != null;
|
||||
}
|
||||
|
||||
if (badCheck(taint)) {
|
||||
sink(taint); // NOT OK
|
||||
}
|
||||
}
|
||||
@@ -38,3 +38,17 @@ class C {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function reflective() {
|
||||
let x = source();
|
||||
|
||||
sink(x); // NOT OK
|
||||
|
||||
if (isSafe.call(x)) {
|
||||
sink(x); // NOT OK - `isSafe` does not sanitize the receiver
|
||||
}
|
||||
|
||||
if (isSafe.call(null, x)) {
|
||||
sink(x); // OK
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user