diff --git a/javascript/ql/src/semmle/javascript/StringOps.qll b/javascript/ql/src/semmle/javascript/StringOps.qll index 4b45a965c2dc..f9818cd46fa9 100644 --- a/javascript/ql/src/semmle/javascript/StringOps.qll +++ b/javascript/ql/src/semmle/javascript/StringOps.qll @@ -112,14 +112,18 @@ module StringOps { } /** - * A call of form `_.startsWith(A, B)` or `ramda.startsWith(A, B)`. + * A call of form `_.startsWith(A, B)` or `ramda.startsWith(A, B)` or `goog.string.startsWith(A, B)`. */ private class StartsWith_Library extends Range, DataFlow::CallNode { StartsWith_Library() { getNumArgument() = 2 and exists(DataFlow::SourceNode callee | this = callee.getACall() | callee = LodashUnderscore::member("startsWith") or - callee = DataFlow::moduleMember("ramda", "startsWith") + callee = DataFlow::moduleMember("ramda", "startsWith") or + exists(string name | + callee = Closure::moduleImport("goog.string." + name) and + (name = "startsWith" or name = "caseInsensitiveStartsWith") + ) ) } @@ -250,6 +254,9 @@ module StringOps { exists(string name | this = LodashUnderscore::member(name).getACall() and (name = "includes" or name = "include" or name = "contains") + or + this = Closure::moduleImport("goog.string." + name).getACall() and + (name = "contains" or name = "caseInsensitiveContains") ) } @@ -416,7 +423,11 @@ module StringOps { getNumArgument() = 2 and exists(DataFlow::SourceNode callee | this = callee.getACall() | callee = LodashUnderscore::member("endsWith") or - callee = DataFlow::moduleMember("ramda", "endsWith") + callee = DataFlow::moduleMember("ramda", "endsWith") or + exists(string name | + callee = Closure::moduleImport("goog.string." + name) and + (name = "endsWith" or name = "caseInsensitiveEndsWith") + ) ) } diff --git a/javascript/ql/test/library-tests/StringOps/EndsWith/EndsWith.expected b/javascript/ql/test/library-tests/StringOps/EndsWith/EndsWith.expected index 210b525c5609..58c0250f4dc1 100644 --- a/javascript/ql/test/library-tests/StringOps/EndsWith/EndsWith.expected +++ b/javascript/ql/test/library-tests/StringOps/EndsWith/EndsWith.expected @@ -1,3 +1,5 @@ -| tst.js:5:7:5:19 | A.endsWith(B) | tst.js:5:7:5:7 | A | tst.js:5:18:5:18 | B | true | -| tst.js:6:7:6:22 | _.endsWith(A, B) | tst.js:6:18:6:18 | A | tst.js:6:21:6:21 | B | true | -| tst.js:7:7:7:22 | R.endsWith(A, B) | tst.js:7:18:7:18 | A | tst.js:7:21:7:21 | B | true | +| tst.js:6:7:6:19 | A.endsWith(B) | tst.js:6:7:6:7 | A | tst.js:6:18:6:18 | B | true | +| tst.js:7:7:7:22 | _.endsWith(A, B) | tst.js:7:18:7:18 | A | tst.js:7:21:7:21 | B | true | +| tst.js:8:7:8:22 | R.endsWith(A, B) | tst.js:8:18:8:18 | A | tst.js:8:21:8:21 | B | true | +| tst.js:9:7:9:28 | strings ... h(A, B) | tst.js:9:24:9:24 | A | tst.js:9:27:9:27 | B | true | +| tst.js:10:7:10:43 | strings ... h(A, B) | tst.js:10:39:10:39 | A | tst.js:10:42:10:42 | B | true | diff --git a/javascript/ql/test/library-tests/StringOps/EndsWith/tst.js b/javascript/ql/test/library-tests/StringOps/EndsWith/tst.js index 149194521fd0..9f0ce1df0ef5 100644 --- a/javascript/ql/test/library-tests/StringOps/EndsWith/tst.js +++ b/javascript/ql/test/library-tests/StringOps/EndsWith/tst.js @@ -1,8 +1,11 @@ import * as _ from 'underscore'; import * as R from 'ramda'; +let strings = goog.require('goog.string'); function test() { if (A.endsWith(B)) {} if (_.endsWith(A, B)) {} if (R.endsWith(A, B)) {} + if (strings.endsWith(A, B)) {} + if (strings.caseInsensitiveEndsWith(A, B)) {} } diff --git a/javascript/ql/test/library-tests/StringOps/Includes/Includes.expected b/javascript/ql/test/library-tests/StringOps/Includes/Includes.expected index 2567e4e6abe3..d5b7dcfdd183 100644 --- a/javascript/ql/test/library-tests/StringOps/Includes/Includes.expected +++ b/javascript/ql/test/library-tests/StringOps/Includes/Includes.expected @@ -1,7 +1,9 @@ -| tst.js:4:7:4:19 | A.includes(B) | tst.js:4:7:4:7 | A | tst.js:4:18:4:18 | B | true | -| tst.js:5:7:5:22 | _.includes(A, B) | tst.js:5:18:5:18 | A | tst.js:5:21:5:21 | B | true | -| tst.js:6:7:6:25 | A.indexOf(B) !== -1 | tst.js:6:7:6:7 | A | tst.js:6:17:6:17 | B | true | -| tst.js:7:7:7:23 | A.indexOf(B) >= 0 | tst.js:7:7:7:7 | A | tst.js:7:17:7:17 | B | true | -| tst.js:8:7:8:19 | ~A.indexOf(B) | tst.js:8:8:8:8 | A | tst.js:8:18:8:18 | B | true | -| tst.js:11:7:11:25 | A.indexOf(B) === -1 | tst.js:11:7:11:7 | A | tst.js:11:17:11:17 | B | false | -| tst.js:12:7:12:22 | A.indexOf(B) < 0 | tst.js:12:7:12:7 | A | tst.js:12:17:12:17 | B | false | +| tst.js:5:7:5:19 | A.includes(B) | tst.js:5:7:5:7 | A | tst.js:5:18:5:18 | B | true | +| tst.js:6:7:6:22 | _.includes(A, B) | tst.js:6:18:6:18 | A | tst.js:6:21:6:21 | B | true | +| tst.js:7:7:7:25 | A.indexOf(B) !== -1 | tst.js:7:7:7:7 | A | tst.js:7:17:7:17 | B | true | +| tst.js:8:7:8:23 | A.indexOf(B) >= 0 | tst.js:8:7:8:7 | A | tst.js:8:17:8:17 | B | true | +| tst.js:9:7:9:19 | ~A.indexOf(B) | tst.js:9:8:9:8 | A | tst.js:9:18:9:18 | B | true | +| tst.js:12:7:12:25 | A.indexOf(B) === -1 | tst.js:12:7:12:7 | A | tst.js:12:17:12:17 | B | false | +| tst.js:13:7:13:22 | A.indexOf(B) < 0 | tst.js:13:7:13:7 | A | tst.js:13:17:13:17 | B | false | +| tst.js:20:7:20:28 | strings ... s(A, B) | tst.js:20:24:20:24 | A | tst.js:20:27:20:27 | B | true | +| tst.js:21:7:21:43 | strings ... s(A, B) | tst.js:21:39:21:39 | A | tst.js:21:42:21:42 | B | true | diff --git a/javascript/ql/test/library-tests/StringOps/Includes/tst.js b/javascript/ql/test/library-tests/StringOps/Includes/tst.js index e0cecf8a54db..fada4e1de29f 100644 --- a/javascript/ql/test/library-tests/StringOps/Includes/tst.js +++ b/javascript/ql/test/library-tests/StringOps/Includes/tst.js @@ -1,4 +1,5 @@ import * as _ from 'lodash'; +let strings = goog.require('goog.string'); function test() { if (A.includes(B)) {} @@ -15,4 +16,7 @@ function test() { if (A.indexOf(B) === 0) {} if (A.indexOf(B) !== 0) {} if (A.indexOf(B) > 0) {} + + if (strings.contains(A, B)) {} + if (strings.caseInsensitiveContains(A, B)) {} } diff --git a/javascript/ql/test/library-tests/StringOps/StartsWith/StartsWith.expected b/javascript/ql/test/library-tests/StringOps/StartsWith/StartsWith.expected index e139bfccbfd8..3910799bd75a 100644 --- a/javascript/ql/test/library-tests/StringOps/StartsWith/StartsWith.expected +++ b/javascript/ql/test/library-tests/StringOps/StartsWith/StartsWith.expected @@ -1,14 +1,16 @@ -| tst.js:5:9:5:23 | A.startsWith(B) | tst.js:5:9:5:9 | A | tst.js:5:22:5:22 | B | true | -| tst.js:6:9:6:26 | _.startsWith(A, B) | tst.js:6:22:6:22 | A | tst.js:6:25:6:25 | B | true | -| tst.js:7:9:7:26 | R.startsWith(A, B) | tst.js:7:22:7:22 | A | tst.js:7:25:7:25 | B | true | -| tst.js:8:9:8:26 | A.indexOf(B) === 0 | tst.js:8:9:8:9 | A | tst.js:8:19:8:19 | B | true | -| tst.js:9:9:9:26 | A.indexOf(B) !== 0 | tst.js:9:9:9:9 | A | tst.js:9:19:9:19 | B | false | -| tst.js:10:9:10:26 | 0 !== A.indexOf(B) | tst.js:10:15:10:15 | A | tst.js:10:25:10:25 | B | false | -| tst.js:11:9:11:25 | 0 != A.indexOf(B) | tst.js:11:14:11:14 | A | tst.js:11:24:11:24 | B | false | -| tst.js:12:9:12:20 | A.indexOf(B) | tst.js:12:9:12:9 | A | tst.js:12:19:12:19 | B | false | -| tst.js:13:10:13:21 | A.indexOf(B) | tst.js:13:10:13:10 | A | tst.js:13:20:13:20 | B | false | -| tst.js:14:11:14:22 | A.indexOf(B) | tst.js:14:11:14:11 | A | tst.js:14:21:14:21 | B | false | -| tst.js:15:9:15:38 | A.subst ... ) === B | tst.js:15:9:15:9 | A | tst.js:15:38:15:38 | B | true | -| tst.js:16:9:16:38 | A.subst ... ) !== B | tst.js:16:9:16:9 | A | tst.js:16:38:16:38 | B | false | -| tst.js:17:9:17:35 | A.subst ... ) === B | tst.js:17:9:17:9 | A | tst.js:17:35:17:35 | B | true | -| tst.js:18:9:18:36 | A.subst ... "web/" | tst.js:18:9:18:9 | A | tst.js:18:31:18:36 | "web/" | true | +| tst.js:6:9:6:23 | A.startsWith(B) | tst.js:6:9:6:9 | A | tst.js:6:22:6:22 | B | true | +| tst.js:7:9:7:26 | _.startsWith(A, B) | tst.js:7:22:7:22 | A | tst.js:7:25:7:25 | B | true | +| tst.js:8:9:8:26 | R.startsWith(A, B) | tst.js:8:22:8:22 | A | tst.js:8:25:8:25 | B | true | +| tst.js:9:9:9:26 | A.indexOf(B) === 0 | tst.js:9:9:9:9 | A | tst.js:9:19:9:19 | B | true | +| tst.js:10:9:10:26 | A.indexOf(B) !== 0 | tst.js:10:9:10:9 | A | tst.js:10:19:10:19 | B | false | +| tst.js:11:9:11:26 | 0 !== A.indexOf(B) | tst.js:11:15:11:15 | A | tst.js:11:25:11:25 | B | false | +| tst.js:12:9:12:25 | 0 != A.indexOf(B) | tst.js:12:14:12:14 | A | tst.js:12:24:12:24 | B | false | +| tst.js:13:9:13:20 | A.indexOf(B) | tst.js:13:9:13:9 | A | tst.js:13:19:13:19 | B | false | +| tst.js:14:10:14:21 | A.indexOf(B) | tst.js:14:10:14:10 | A | tst.js:14:20:14:20 | B | false | +| tst.js:15:11:15:22 | A.indexOf(B) | tst.js:15:11:15:11 | A | tst.js:15:21:15:21 | B | false | +| tst.js:16:9:16:38 | A.subst ... ) === B | tst.js:16:9:16:9 | A | tst.js:16:38:16:38 | B | true | +| tst.js:17:9:17:38 | A.subst ... ) !== B | tst.js:17:9:17:9 | A | tst.js:17:38:17:38 | B | false | +| tst.js:18:9:18:35 | A.subst ... ) === B | tst.js:18:9:18:9 | A | tst.js:18:35:18:35 | B | true | +| tst.js:19:9:19:36 | A.subst ... "web/" | tst.js:19:9:19:9 | A | tst.js:19:31:19:36 | "web/" | true | +| tst.js:32:9:32:32 | strings ... h(A, B) | tst.js:32:28:32:28 | A | tst.js:32:31:32:31 | B | true | +| tst.js:33:9:33:47 | strings ... h(A, B) | tst.js:33:43:33:43 | A | tst.js:33:46:33:46 | B | true | diff --git a/javascript/ql/test/library-tests/StringOps/StartsWith/tst.js b/javascript/ql/test/library-tests/StringOps/StartsWith/tst.js index 3a64cd8bbfd6..97bbf240ec62 100644 --- a/javascript/ql/test/library-tests/StringOps/StartsWith/tst.js +++ b/javascript/ql/test/library-tests/StringOps/StartsWith/tst.js @@ -1,5 +1,6 @@ import * as _ from 'lodash'; import * as R from 'ramda'; +let strings = goog.require('goog.string'); function f(A, B) { if (A.startsWith(B)) {} @@ -27,4 +28,7 @@ function f(A, B) { if (A.indexOf(B, 2)) {} if (~A.indexOf(B)) {} // checks for existence, not startsWith if (A.substring(B.length) === 0) {} + + if (strings.startsWith(A, B)) {} + if (strings.caseInsensitiveStartsWith(A, B)) {} }