diff --git a/change-notes/1.24/analysis-javascript.md b/change-notes/1.24/analysis-javascript.md index 003a19d1182..be33d77204f 100644 --- a/change-notes/1.24/analysis-javascript.md +++ b/change-notes/1.24/analysis-javascript.md @@ -24,6 +24,7 @@ - [lazy-cache](https://www.npmjs.com/package/lazy-cache) - [for-in](https://www.npmjs.com/package/for-in) - [for-own](https://www.npmjs.com/package/for-own) + - [send](https://www.npmjs.com/package/send) ## New queries diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 4b4df49ed66..25bb232f8fe 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -428,4 +428,11 @@ module TaintedPath { class AngularJSTemplateUrlSink extends Sink, DataFlow::ValueNode { AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") } } + + /** + * The path argument of a [send](https://www.npmjs.com/package/send) call, viewed as a sink. + */ + class SendPathSink extends Sink, DataFlow::ValueNode { + SendPathSink() { this = DataFlow::moduleImport("send").getACall().getArgument(1) } + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 5893cfb99eb..7e2aa30fe1f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -801,6 +801,92 @@ nodes | TaintedPath.js:112:45:112:52 | realpath | | TaintedPath.js:112:45:112:52 | realpath | | TaintedPath.js:112:45:112:52 | realpath | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:23:119:29 | req.url | +| TaintedPath.js:119:23:119:29 | req.url | +| TaintedPath.js:119:23:119:29 | req.url | +| TaintedPath.js:119:23:119:29 | req.url | +| TaintedPath.js:119:23:119:29 | req.url | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:121:23:121:26 | path | | normalizedPaths.js:11:7:11:27 | path | | normalizedPaths.js:11:7:11:27 | path | | normalizedPaths.js:11:7:11:27 | path | @@ -2996,6 +3082,118 @@ edges | TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | | TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | | TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:6:119:47 | path | TaintedPath.js:121:23:121:26 | path | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:36 | url.par ... , true) | TaintedPath.js:119:13:119:42 | url.par ... ).query | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:42 | url.par ... ).query | TaintedPath.js:119:13:119:47 | url.par ... ry.path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:13:119:47 | url.par ... ry.path | TaintedPath.js:119:6:119:47 | path | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | +| TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:119:13:119:36 | url.par ... , true) | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | | normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path | @@ -4171,6 +4369,7 @@ edges | TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | This path depends on $@. | TaintedPath.js:94:48:94:60 | req.params[0] | a user-provided value | | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value | | TaintedPath.js:112:45:112:52 | realpath | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:112:45:112:52 | realpath | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value | +| TaintedPath.js:121:23:121:26 | path | TaintedPath.js:119:23:119:29 | req.url | TaintedPath.js:121:23:121:26 | path | This path depends on $@. | TaintedPath.js:119:23:119:29 | req.url | a user-provided value | | normalizedPaths.js:13:19:13:22 | path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:13:19:13:22 | path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | | normalizedPaths.js:14:19:14:29 | './' + path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:14:19:14:29 | './' + path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | | normalizedPaths.js:15:19:15:38 | path + '/index.html' | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:15:19:15:38 | path + '/index.html' | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index ece2d44a113..36604536686 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -114,3 +114,10 @@ var server = http.createServer(function(req, res) { ); }); + +var server = http.createServer(function(req, res) { + let path = url.parse(req.url, true).query.path; + + require('send')(req, path); // NOT OK + +});