diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll index 88577a71c5b9..4e13cda58839 100644 --- a/javascript/ql/lib/semmle/javascript/PackageExports.qll +++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll @@ -141,6 +141,19 @@ private DataFlow::Node getAValueExportedByPackage() { result = unique( | | call.getCalleeNode().getAFunctionValue()).getAReturn() ) or + // the exported value is a function that returns another import. + // ```JavaScript + // module.exports = function foo() { + // return require("./other-module.js"); + // } + // ``` + exists(DataFlow::FunctionNode func, Module mod | + func = getAValueExportedByPackage().getABoundFunctionValue(_) + | + mod = func.getAReturn().getALocalSource().getEnclosingExpr().(Import).getImportedModule() and + result = getAnExportFromModule(mod) + ) + or // ***** // Common styles of transforming exported objects. // ***** diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index dbf74f78d8f0..378c6ac5ef8c 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -28,6 +28,7 @@ | jsonschema.js:15:23:15:29 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b | | jsonschema.js:20:18:20:24 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b | | lib/closure.js:4:6:4:7 | u* | Strings with many repetitions of 'u' can start matching anywhere after the start of the preceeding u*o | +| lib/indirect.js:2:6:2:7 | k* | Strings with many repetitions of 'k' can start matching anywhere after the start of the preceeding k*h | | lib/lib.js:1:15:1:16 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/lib.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected index bd49ef91504a..9a7e42517569 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected @@ -3,6 +3,10 @@ nodes | lib/closure.js:3:21:3:21 | x | | lib/closure.js:4:16:4:16 | x | | lib/closure.js:4:16:4:16 | x | +| lib/indirect.js:1:32:1:32 | x | +| lib/indirect.js:1:32:1:32 | x | +| lib/indirect.js:2:16:2:16 | x | +| lib/indirect.js:2:16:2:16 | x | | lib/lib.js:3:28:3:31 | name | | lib/lib.js:3:28:3:31 | name | | lib/lib.js:4:14:4:17 | name | @@ -170,6 +174,10 @@ edges | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | +| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | +| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | +| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | +| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | @@ -329,6 +337,7 @@ edges | polynomial-redos.js:123:13:123:20 | replaced | polynomial-redos.js:123:3:123:20 | result | #select | lib/closure.js:4:5:4:17 | /u*o/.test(x) | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'u'. | lib/closure.js:4:6:4:7 | u* | regular expression | lib/closure.js:3:21:3:21 | x | library input | +| lib/indirect.js:2:5:2:17 | /k*h/.test(x) | lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'k'. | lib/indirect.js:2:6:2:7 | k* | regular expression | lib/indirect.js:1:32:1:32 | x | library input | | lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input | | lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input | | lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/indirect.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/indirect.js new file mode 100644 index 000000000000..711e02eeeccb --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/indirect.js @@ -0,0 +1,3 @@ +module.exports.foo = function (x) { + /k*h/.test(x); // NOT OK +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js index d18f43078c3d..d62f18834827 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js @@ -12,4 +12,8 @@ if (typeof define !== 'undefined' && define.amd) { // AMD define([], function () {return bar}); } -module.exports.closure = require("./closure") \ No newline at end of file +module.exports.closure = require("./closure") + +module.exports.func = function (conf) { + return require("./indirect") +} \ No newline at end of file