diff --git a/javascript/ql/src/Performance/PolynomialReDoS.ql b/javascript/ql/src/Performance/PolynomialReDoS.ql index f77a312ed9c..393faef10dd 100644 --- a/javascript/ql/src/Performance/PolynomialReDoS.ql +++ b/javascript/ql/src/Performance/PolynomialReDoS.ql @@ -25,4 +25,5 @@ where sink.getNode().(Sink).getRegExp().(PolynomialBackTrackingTerm).isAtEndLine() ) select sink.getNode(), source, sink, "This expensive $@ use depends on $@.", - sink.getNode().(Sink).getRegExp(), "regular expression", source.getNode(), "a user-provided value" + sink.getNode().(Sink).getRegExp(), "regular expression", source.getNode(), + source.getNode().(Source).describe() diff --git a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll index 3392342db6b..bcd267d8487 100644 --- a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll @@ -13,10 +13,17 @@ module PolynomialReDoS { */ abstract class Source extends DataFlow::Node { /** - * Gets the kind of source that is being accesed. See `HTTP::RequestInputAccess::getKind()`. - * Can be one of "parameter", "header", "body", "url", "cookie". + * Gets the kind of source that is being accesed. + * + * Is either a kind from `HTTP::RequestInputAccess::getKind()`, or "library". */ abstract string getKind(); + + /** + * Gets a string that describes the source. + * For use in the alert message + */ + string describe() { result = "a user-provided value" } } /** @@ -108,4 +115,24 @@ module PolynomialReDoS { e = input.asExpr() } } + + private import semmle.javascript.PackageExports as Exports + + /** + * A parameter of an exported function, seen as a source for polynomial-redos. + */ + class ExternalInputSource extends Source, DataFlow::ParameterNode { + ExternalInputSource() { + exists(int bound, DataFlow::FunctionNode func | + func = + Exports::getAValueExportedBy(Exports::getTopmostPackageJSON()) + .getABoundFunctionValue(bound) and + this = func.getParameter(any(int arg | arg >= bound)) + ) + } + + override string getKind() { result = "library" } + + override string describe() { result = "library input" } + } } diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index ee861fb16d8..c351f3f902e 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -24,6 +24,7 @@ | highlight.js:38:54:38:59 | [^()]* | Strings starting with 'A((' and with many repetitions of ''' can start matching anywhere after the start of the preceeding [^()]* | | highlight.js:38:64:38:69 | [^()]* | Strings starting with 'A(' and with many repetitions of ''' can start matching anywhere after the start of the preceeding [^()]* | | highlight.js:39:22:39:24 | \\w* | Strings starting with 'A' and with many repetitions of 'A' can start matching anywhere after the start of the preceeding [a-zA-Z_]\\w*\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{ | +| 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 | | polynomial-redos.js:7:24:7:26 | \\s+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+$ | | polynomial-redos.js:8:17:8:18 | * | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding *, * | | polynomial-redos.js:9:19:9:21 | \\s* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s*\\n\\s* | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected index 70e4720bcfe..71990d6b919 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected @@ -1,4 +1,8 @@ nodes +| lib/lib.js:3:28:3:31 | name | +| lib/lib.js:3:28:3:31 | name | +| lib/lib.js:4:14:4:17 | name | +| lib/lib.js:4:14:4:17 | name | | polynomial-redos.js:5:6:5:32 | tainted | | polynomial-redos.js:5:16:5:32 | req.query.tainted | | polynomial-redos.js:5:16:5:32 | req.query.tainted | @@ -135,6 +139,10 @@ nodes | polynomial-redos.js:118:2:118:8 | tainted | | polynomial-redos.js:118:2:118:8 | tainted | edges +| 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 | +| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:8:2:8:8 | tainted | @@ -266,6 +274,7 @@ edges | polynomial-redos.js:68:18:68:24 | req.url | polynomial-redos.js:68:18:68:24 | req.url | | polynomial-redos.js:69:18:69:25 | req.body | polynomial-redos.js:69:18:69:25 | req.body | #select +| lib/lib.js:4:14:4:17 | name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This expensive $@ use depends on $@. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input | | polynomial-redos.js:7:2:7:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:7:2:7:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:7:24:7:26 | \\s+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:8:2:8:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:8:2:8:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:8:17:8:18 | * | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:9:2:9:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:9:2:9:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:9:19:9:21 | \\s* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js new file mode 100644 index 00000000000..23ba27a32c2 --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/lib.js @@ -0,0 +1,5 @@ +var regexp = /a*b/; + +module.exports = function (name) { + regexp.test(name); // NOT OK +}; diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/package.json b/javascript/ql/test/query-tests/Performance/ReDoS/package.json new file mode 100644 index 00000000000..27e480ea5c1 --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/package.json @@ -0,0 +1,5 @@ +{ + "name": "myLib", + "version": "0.0.7", + "main": "./lib/lib.js" +} \ No newline at end of file