JS: introduce MembershipTests.qll and use in two locations

This commit is contained in:
Esben Sparre Andreasen
2020-04-28 20:35:33 +02:00
parent 6041d52936
commit ddb545c182
13 changed files with 395 additions and 26 deletions

View File

@@ -10,6 +10,9 @@
- [marsdb](https://www.npmjs.com/package/marsdb) - [marsdb](https://www.npmjs.com/package/marsdb)
- [minimongo](https://www.npmjs.com/package/minimongo/) - [minimongo](https://www.npmjs.com/package/minimongo/)
* The analysis of sanitizers has improved, leading to more accurate
results from the security queries.
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |

View File

@@ -61,18 +61,11 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
sw.getSubstring().mayHaveStringValue(scheme) sw.getSubstring().mayHaveStringValue(scheme)
) )
or or
// check of the form `array.includes(getScheme(nd))` exists(DataFlow::Node candidate, MembershipTest t |
exists(InclusionTest test, DataFlow::ArrayCreationNode array | test = result | result = t and
schemeOf(nd).flowsTo(test.getContainedNode()) and t.getCandidate() = candidate and
array.flowsTo(test.getContainerNode()) and t.getAMemberString() = scheme.getWithOrWithoutColon() and
array.getAnElement().mayHaveStringValue(scheme.getWithOrWithoutColon()) schemeOf(nd).flowsTo(candidate)
)
or
// check of the form `getScheme(nd) === scheme`
exists(EqualityTest test, Expr op1, Expr op2 | test.flow() = result |
test.hasOperands(op1, op2) and
schemeOf(nd).flowsToExpr(op1) and
op2.mayHaveStringValue(scheme.getWithOrWithoutColon())
) )
or or
// propagate through trimming, case conversion, and regexp replace // propagate through trimming, case conversion, and regexp replace

View File

@@ -449,8 +449,10 @@ class BlacklistInclusionGuard extends DataFlow::LabeledBarrierGuardNode, Inclusi
*/ */
class WhitelistInclusionGuard extends DataFlow::LabeledBarrierGuardNode { class WhitelistInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
WhitelistInclusionGuard() { WhitelistInclusionGuard() {
this instanceof TaintTracking::PositiveIndexOfSanitizer or this instanceof TaintTracking::PositiveIndexOfSanitizer
this instanceof TaintTracking::InclusionSanitizer or
this instanceof TaintTracking::MembershipTestSanitizer and
not this instanceof MembershipTest::ObjectPropertyNameMembershipTest // handled with more precision in `HasOwnPropertyGuard`
} }
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) { override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {

View File

@@ -37,6 +37,7 @@ import semmle.javascript.JsonParsers
import semmle.javascript.JSX import semmle.javascript.JSX
import semmle.javascript.Lines import semmle.javascript.Lines
import semmle.javascript.Locations import semmle.javascript.Locations
import semmle.javascript.MembershipTests
import semmle.javascript.Modules import semmle.javascript.Modules
import semmle.javascript.NodeJS import semmle.javascript.NodeJS
import semmle.javascript.NPM import semmle.javascript.NPM

View File

@@ -5,7 +5,7 @@
private import javascript private import javascript
/** /**
* A expression that checks if an element is contained in an array * An expression that checks if an element is contained in an array
* or is a substring of another string. * or is a substring of another string.
* *
* Examples: * Examples:

View File

@@ -0,0 +1,257 @@
/**
* Provides classes for recognizing membership tests.
*/
import javascript
/**
* An expression that tests if a candidate is a member of a collection.
*
* Additional tests can be added by subclassing `MembershipTest::Range`
*/
class MembershipTest extends DataFlow::Node {
MembershipTest::Range range;
MembershipTest() { this = range }
/**
* Gets the candidate of this test.
*/
DataFlow::Node tests() { result = range.tests() }
/**
* Gets a string that is a member of the collection of this test, if
* it can be determined.
*/
string getAMemberString() { result = range.getAMemberString() }
/**
* Gets a node that is a member of the collection of this test, if
* it can be determined.
*/
DataFlow::Node getAMemberNode() { result = range.getAMemberNode() }
/**
* Gets the polarity of this test.
*
* If the polarity is `false` the test returns `true` if the
* collection does not contain the candidate.
*/
boolean getPolarity() { result = range.getPolarity() }
}
/**
* Provides classes for recognizing membership tests.
*/
module MembershipTest {
/**
* An expression that tests if a candidate is a member of a collection.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the candidate of this test.
*/
abstract DataFlow::Node tests();
/**
* Gets a string that is a member of the collection of this test, if
* it can be determined.
*/
string getAMemberString() { this.getAMemberNode().mayHaveStringValue(result) }
/**
* Gets a node that is a member of the collection of this test, if
* it can be determined.
*/
DataFlow::Node getAMemberNode() { none() }
/**
* Gets the polarity of this test.
*
* If the polarity is `false` the test returns `true` if the
* collection does not contain the candidate.
*/
boolean getPolarity() { result = true }
}
/**
* An `InclusionTest` viewed as a `MembershipTest`.
*/
private class OrdinaryInclusionTest extends InclusionTest, MembershipTest::Range {
override DataFlow::Node tests() { result = this.getContainedNode() }
override boolean getPolarity() { result = InclusionTest.super.getPolarity() }
}
/**
* A test for whether a candidate is a member of an array.
*/
class ArrayMembershipTest extends OrdinaryInclusionTest {
DataFlow::ArrayCreationNode array;
ArrayMembershipTest() { array.flowsTo(this.getContainerNode()) }
/**
* Gets the array of this test.
*/
DataFlow::ArrayCreationNode getArray() { result = array }
override DataFlow::Node getAMemberNode() { result = array.getAnElement() }
}
/**
* A test for whether a candidate is a member of an array constructed
* from a call to `String.prototype.split`.
*/
private class ShorthandArrayMembershipTest extends OrdinaryInclusionTest {
DataFlow::MethodCallNode split;
ShorthandArrayMembershipTest() {
split.getMethodName() = "split" and
split.getNumArgument() = [1, 2] and
split.flowsTo(this.getContainerNode())
}
override string getAMemberString() {
exists(string toSplit |
split.getReceiver().getStringValue() = toSplit and
result = toSplit.splitAt(split.getArgument(0).getStringValue())
)
}
}
/**
* An `EqualityTest` viewed as a `MembershipTest`.
*/
private class EqualityLeftMembershipTest extends MembershipTest::Range, DataFlow::ValueNode {
override EqualityTest astNode;
override DataFlow::Node tests() { astNode.getLeftOperand() = result.asExpr() }
override DataFlow::Node getAMemberNode() { result = astNode.getRightOperand().flow() }
override boolean getPolarity() { result = astNode.getPolarity() }
}
/**
* An `EqualityTest` viewed as a `MembershipTest`.
*/
private class EqualityRightMembershipTest extends MembershipTest::Range, DataFlow::ValueNode {
override EqualityTest astNode;
override DataFlow::Node tests() { astNode.getRightOperand() = result.asExpr() }
override DataFlow::Node getAMemberNode() { result = astNode.getLeftOperand().flow() }
override boolean getPolarity() { result = astNode.getPolarity() }
}
/**
* A regular expression that enumerates all of its matched strings.
*/
private class EnumerationRegExp extends RegExpTerm {
EnumerationRegExp() {
this.isRootTerm() and
RegExp::isFullyAnchoredTerm(this) and
exists(RegExpTerm child | this.getAChild*() = child |
child instanceof RegExpSequence or
child instanceof RegExpCaret or
child instanceof RegExpDollar or
child instanceof RegExpConstant or
child instanceof RegExpAlt or
child instanceof RegExpGroup
)
}
/**
* Gets a string matched by this regular expression.
*/
string getAMember() { result = this.getAChild*().getAMatchedString() }
}
/**
* A test for whether a string is matched by a regular expression that
* enumerates all of its matched strings.
*/
private class RegExpEnumerationTest extends MembershipTest::Range, DataFlow::Node {
EnumerationRegExp enumeration;
DataFlow::Node candidateNode;
boolean polarity;
RegExpEnumerationTest() {
exists(
DataFlow::Node tests, DataFlow::MethodCallNode mcn, DataFlow::Node base, string m,
DataFlow::Node firstArg
|
(
this = tests and
any(ConditionGuardNode g).getTest() = tests.asExpr() and
polarity = true
or
exists(EqualityTest eq, Expr null |
eq.flow() = this and
polarity = eq.getPolarity().booleanNot() and
eq.hasOperands(tests.asExpr(), null) and
SyntacticConstants::isNull(null)
)
) and
mcn.flowsTo(tests) and
mcn.calls(base, m) and
firstArg = mcn.getArgument(0)
|
// /re/.test(u) or /re/.exec(u)
enumeration = RegExp::getRegExpObjectFromNode(base) and
(m = "test" or m = "exec") and
firstArg = candidateNode
or
// u.match(/re/) or u.match("re")
base = candidateNode and
m = "match" and
enumeration = RegExp::getRegExpFromNode(firstArg)
)
}
override DataFlow::Node tests() { result = candidateNode }
override string getAMemberString() { result = enumeration.getAMember() }
override boolean getPolarity() { result = polarity }
}
/**
* An expression that tests if a candidate is a member of a collection class, such as a map or set.
*/
class CollectionMembershipTest extends MembershipTest::Range, DataFlow::MethodCallNode {
CollectionMembershipTest() { getMethodName() = "has" }
override DataFlow::Node tests() { result = getArgument(0) }
}
/**
* An expression that tests if a candidate is a property name of an object.
*/
class ObjectPropertyNameMembershipTest extends MembershipTest::Range, DataFlow::ValueNode {
DataFlow::ValueNode candidateNode;
DataFlow::ValueNode membersNode;
ObjectPropertyNameMembershipTest() {
exists(InExpr inExpr |
astNode = inExpr and
inExpr.getLeftOperand() = candidateNode.asExpr() and
inExpr.getRightOperand() = membersNode.asExpr()
)
or
exists(DataFlow::MethodCallNode hasOwn |
this = hasOwn and
hasOwn.calls(membersNode, "hasOwnProperty") and
hasOwn.getArgument(0) = candidateNode
)
}
override DataFlow::Node tests() { result = candidateNode }
override string getAMemberString() {
exists(membersNode.getALocalSource().getAPropertyWrite(result))
}
}
}

View File

@@ -825,18 +825,22 @@ module TaintTracking {
override predicate appliesTo(Configuration cfg) { any() } override predicate appliesTo(Configuration cfg) { any() }
} }
/** DEPRECATED. This class has been renamed to `InclusionSanitizer`. */ /** DEPRECATED. This class has been renamed to `MembershipTestSanitizer`. */
deprecated class StringInclusionSanitizer = InclusionSanitizer; deprecated class StringInclusionSanitizer = MembershipTestSanitizer;
/** A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch. */ /** DEPRECATED. This class has been renamed to `MembershipTestSanitizer`. */
class InclusionSanitizer extends AdditionalSanitizerGuardNode { deprecated class InclusionSanitizer = MembershipTestSanitizer;
InclusionTest inclusion;
InclusionSanitizer() { this = inclusion } /**
* A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch.
*/
class MembershipTestSanitizer extends AdditionalSanitizerGuardNode {
MembershipTest test;
MembershipTestSanitizer() { this = test }
override predicate sanitizes(boolean outcome, Expr e) { override predicate sanitizes(boolean outcome, Expr e) {
outcome = inclusion.getPolarity() and test.getCandidate() = e.flow() and test.getPolarity() = outcome
e = inclusion.getContainedNode().asExpr()
} }
override predicate appliesTo(Configuration cfg) { any() } override predicate appliesTo(Configuration cfg) { any() }
@@ -871,8 +875,12 @@ module TaintTracking {
/** Gets a variable that is defined exactly once. */ /** Gets a variable that is defined exactly once. */
private Variable singleDef() { strictcount(result.getADefinition()) = 1 } private Variable singleDef() { strictcount(result.getADefinition()) = 1 }
/** A check of the form `if(x == 'some-constant')`, which sanitizes `x` in its "then" branch. */ /**
class ConstantComparison extends AdditionalSanitizerGuardNode, DataFlow::ValueNode { * A check of the form `if(x == 'some-constant')`, which sanitizes `x` in its "then" branch.
*
* DEPRECATED: use `MembershipTests::MembershipTest` instead.
*/
deprecated class ConstantComparison extends SanitizerGuardNode, DataFlow::ValueNode {
Expr x; Expr x;
override EqualityTest astNode; override EqualityTest astNode;
@@ -890,7 +898,10 @@ module TaintTracking {
outcome = astNode.getPolarity() and x = e outcome = astNode.getPolarity() and x = e
} }
override predicate appliesTo(Configuration cfg) { any() } /**
* Holds if this guard applies to the flow in `cfg`.
*/
predicate appliesTo(Configuration cfg) { any() }
} }
/** /**

View File

@@ -4,16 +4,31 @@
| tst.js:35:9:35:14 | v in o | ExampleConfiguration | true | tst.js:35:9:35:9 | v | | tst.js:35:9:35:14 | v in o | ExampleConfiguration | true | tst.js:35:9:35:9 | v |
| tst.js:47:9:47:25 | o[v] == undefined | ExampleConfiguration | false | tst.js:47:11:47:11 | v | | tst.js:47:9:47:25 | o[v] == undefined | ExampleConfiguration | false | tst.js:47:11:47:11 | v |
| tst.js:47:9:47:25 | o[v] == undefined | ExampleConfiguration | true | tst.js:47:9:47:12 | o[v] | | tst.js:47:9:47:25 | o[v] == undefined | ExampleConfiguration | true | tst.js:47:9:47:12 | o[v] |
| tst.js:47:9:47:25 | o[v] == undefined | ExampleConfiguration | true | tst.js:47:17:47:25 | undefined |
| tst.js:53:9:53:26 | undefined === o[v] | ExampleConfiguration | false | tst.js:53:25:53:25 | v | | tst.js:53:9:53:26 | undefined === o[v] | ExampleConfiguration | false | tst.js:53:25:53:25 | v |
| tst.js:53:9:53:26 | undefined === o[v] | ExampleConfiguration | true | tst.js:53:9:53:17 | undefined |
| tst.js:53:9:53:26 | undefined === o[v] | ExampleConfiguration | true | tst.js:53:23:53:26 | o[v] | | tst.js:53:9:53:26 | undefined === o[v] | ExampleConfiguration | true | tst.js:53:23:53:26 | o[v] |
| tst.js:59:9:59:26 | o[v] !== undefined | ExampleConfiguration | false | tst.js:59:9:59:12 | o[v] | | tst.js:59:9:59:26 | o[v] !== undefined | ExampleConfiguration | false | tst.js:59:9:59:12 | o[v] |
| tst.js:59:9:59:26 | o[v] !== undefined | ExampleConfiguration | false | tst.js:59:18:59:26 | undefined |
| tst.js:59:9:59:26 | o[v] !== undefined | ExampleConfiguration | true | tst.js:59:11:59:11 | v | | tst.js:59:9:59:26 | o[v] !== undefined | ExampleConfiguration | true | tst.js:59:11:59:11 | v |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | false | tst.js:71:9:71:20 | o.indexOf(v) |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | false | tst.js:71:19:71:19 | v | | tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | false | tst.js:71:19:71:19 | v |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | false | tst.js:71:25:71:26 | -1 |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | true | tst.js:71:9:71:20 | o.indexOf(v) | | tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | true | tst.js:71:9:71:20 | o.indexOf(v) |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | true | tst.js:71:19:71:19 | v |
| tst.js:71:9:71:26 | o.indexOf(v) == -1 | ExampleConfiguration | true | tst.js:71:25:71:26 | -1 |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | false | tst.js:77:9:77:10 | -1 |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | false | tst.js:77:16:77:27 | o.indexOf(v) |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | false | tst.js:77:26:77:26 | v | | tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | false | tst.js:77:26:77:26 | v |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | true | tst.js:77:9:77:10 | -1 |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | true | tst.js:77:16:77:27 | o.indexOf(v) | | tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | true | tst.js:77:16:77:27 | o.indexOf(v) |
| tst.js:77:9:77:27 | -1 === o.indexOf(v) | ExampleConfiguration | true | tst.js:77:26:77:26 | v |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | false | tst.js:83:9:83:20 | o.indexOf(v) | | tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | false | tst.js:83:9:83:20 | o.indexOf(v) |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | false | tst.js:83:19:83:19 | v |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | false | tst.js:83:26:83:27 | -1 |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | true | tst.js:83:9:83:20 | o.indexOf(v) |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | true | tst.js:83:19:83:19 | v | | tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | true | tst.js:83:19:83:19 | v |
| tst.js:83:9:83:27 | o.indexOf(v) !== -1 | ExampleConfiguration | true | tst.js:83:26:83:27 | -1 |
| tst.js:95:9:95:21 | o.contains(v) | ExampleConfiguration | true | tst.js:95:20:95:20 | v | | tst.js:95:9:95:21 | o.contains(v) | ExampleConfiguration | true | tst.js:95:20:95:20 | v |
| tst.js:107:9:107:16 | o.has(v) | ExampleConfiguration | true | tst.js:107:15:107:15 | v | | tst.js:107:9:107:16 | o.has(v) | ExampleConfiguration | true | tst.js:107:15:107:15 | v |
| tst.js:119:9:119:21 | o.includes(v) | ExampleConfiguration | true | tst.js:119:20:119:20 | v | | tst.js:119:9:119:21 | o.includes(v) | ExampleConfiguration | true | tst.js:119:20:119:20 | v |
@@ -65,4 +80,6 @@
| 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:32:356:34 | x10 |
| tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:40:356:48 | undefined | | tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:40:356:48 | undefined |
| tst.js:370:9:370:29 | o.p == ... listed" | ExampleConfiguration | true | tst.js:370:9:370:11 | o.p | | tst.js:370:9:370:29 | o.p == ... listed" | ExampleConfiguration | true | tst.js:370:9:370:11 | o.p |
| tst.js:370:9:370:29 | o.p == ... listed" | ExampleConfiguration | true | tst.js:370:16:370:29 | "white-listed" |
| tst.js:377:11:377:32 | o[p] == ... listed" | ExampleConfiguration | true | tst.js:377:11:377:14 | o[p] | | tst.js:377:11:377:32 | o[p] == ... listed" | ExampleConfiguration | true | tst.js:377:11:377:14 | o[p] |
| tst.js:377:11:377:32 | o[p] == ... listed" | ExampleConfiguration | true | tst.js:377:19:377:32 | "white-listed" |

View File

@@ -95,6 +95,9 @@ typeInferenceMismatch
| sanitizer-guards.js:68:11:68:18 | source() | sanitizer-guards.js:75:8:75:8 | x | | sanitizer-guards.js:68:11:68:18 | source() | sanitizer-guards.js:75:8:75:8 | x |
| sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x |
| sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:93:8:93:8 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:98:7:98:7 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:104:7:104:7 | x |
| spread.js:2:15:2:22 | source() | spread.js:4:8:4:19 | { ...taint } | | 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:5:8:5:43 | { f: 'h ... orld' } |
| spread.js:2:15:2:22 | source() | spread.js:7:8:7:19 | [ ...taint ] | | spread.js:2:15:2:22 | source() | spread.js:7:8:7:19 | [ ...taint ] |

View File

@@ -71,6 +71,11 @@
| sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x |
| sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x |
| sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:86:7:86:7 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:86:7:86:7 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:93:8:93:8 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:96:10:96:10 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:98:7:98:7 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:102:10:102:10 | x |
| sanitizer-guards.js:91:11:91:18 | source() | sanitizer-guards.js:104:7:104:7 | x |
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field | | 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 | | 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 | | tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |

View File

@@ -86,3 +86,21 @@ function falsy() {
sink(x); // NOT OK sink(x); // NOT OK
} }
} }
function comparisons() {
let x = source();
sink(x); // NOT OK
if (x === "foo") {
sink(x); // OK
} else {
sink(x); // NOT OK
}
if (x === something()) {
sink(x); // OK
} else {
sink(x); // NOT OK
}
}

View File

@@ -4,3 +4,9 @@
| IncompleteUrlSchemeCheck.js:30:9:30:43 | badProt ... scheme) | This check does not consider vbscript:. | | IncompleteUrlSchemeCheck.js:30:9:30:43 | badProt ... scheme) | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:37:9:37:31 | scheme ... script" | This check does not consider data: and vbscript:. | | IncompleteUrlSchemeCheck.js:37:9:37:31 | scheme ... script" | This check does not consider data: and vbscript:. |
| IncompleteUrlSchemeCheck.js:51:9:51:31 | scheme ... script" | This check does not consider data: and vbscript:. | | IncompleteUrlSchemeCheck.js:51:9:51:31 | scheme ... script" | This check does not consider data: and vbscript:. |
| IncompleteUrlSchemeCheck.js:58:6:58:56 | "javasc ... !== -1 | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:65:6:65:28 | "javasc ... scheme | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:72:6:72:48 | /^(java ... == null | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:79:6:79:48 | /^(java ... == null | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:87:7:87:40 | /^(java ... scheme) | This check does not consider vbscript:. |
| IncompleteUrlSchemeCheck.js:104:6:104:39 | /^(java ... scheme) | This check does not consider vbscript:. |

View File

@@ -52,3 +52,56 @@ function test7(url) {
return "about:blank"; return "about:blank";
return url; return url;
} }
function test8(url) {
let scheme = goog.uri.utils.getScheme(url);
if ("javascript|data".split("|").indexOf(scheme) !== -1) // NOT OK
return "about:blank";
return url;
}
function test9(url) {
let scheme = goog.uri.utils.getScheme(url);
if ("javascript" === scheme || "data" === scheme) // NOT OK
return "about:blank";
return url;
}
function test10(url) {
let scheme = goog.uri.utils.getScheme(url);
if (/^(javascript|data)$/.exec(scheme) !== null) // NOT OK
return "about:blank";
return url;
}
function test11(url) {
let scheme = goog.uri.utils.getScheme(url);
if (/^(javascript|data)$/.exec(scheme) === null) // NOT OK
return url;
return "about:blank";
}
function test12(url) {
let scheme = goog.uri.utils.getScheme(url);
if (!/^(javascript|data)$/.exec(scheme)) // NOT OK
return url;
return "about:blank";
}
function test13(url) {
let scheme = goog.uri.utils.getScheme(url);
switch (scheme) {
case "javascript": // NOT OK - but not detected due to lack of `switch` support
case "data":
return "about:blank";
default:
return url;
}
}
function test14(url) {
let scheme = goog.uri.utils.getScheme(url);
if (/^(javascript|data)$/.exec(scheme)) // NOT OK
return "about:blank";
return url;
}