From dcc73a7f90c7b7b4c22a36807cac820cebc08aa4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 5 Oct 2023 09:21:46 +0200 Subject: [PATCH] JS: Port RegExpInjection --- .../dataflow/RegExpInjectionQuery.qll | 18 +- .../src/Security/CWE-730/RegExpInjection.ql | 6 +- .../Security/CWE-730/RegExpInjection.expected | 179 +++++++----------- 3 files changed, 87 insertions(+), 116 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionQuery.qll index 00fe3779e12..476fd9ccd85 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionQuery.qll @@ -13,7 +13,23 @@ import RegExpInjectionCustomizations::RegExpInjection /** * A taint-tracking configuration for untrusted user input used to construct regular expressions. */ -class Configuration extends TaintTracking::Configuration { +module RegExpInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } +} + +/** + * Taint-tracking for untrusted user input used to construct regular expressions. + */ +module RegExpInjectionFlow = TaintTracking::Global; + +/** + * DEPRECATED. Use the `RegExpInjectionFlow` module instead. + */ +deprecated class Configuration extends TaintTracking::Configuration { Configuration() { this = "RegExpInjection" } override predicate isSource(DataFlow::Node source) { source instanceof Source } diff --git a/javascript/ql/src/Security/CWE-730/RegExpInjection.ql b/javascript/ql/src/Security/CWE-730/RegExpInjection.ql index 5b679cf1dcf..4260c5e23ee 100644 --- a/javascript/ql/src/Security/CWE-730/RegExpInjection.ql +++ b/javascript/ql/src/Security/CWE-730/RegExpInjection.ql @@ -15,9 +15,9 @@ import javascript import semmle.javascript.security.dataflow.RegExpInjectionQuery -import DataFlow::PathGraph +import RegExpInjectionFlow::PathGraph -from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) +from RegExpInjectionFlow::PathNode source, RegExpInjectionFlow::PathNode sink +where RegExpInjectionFlow::flowPath(source, sink) select sink.getNode(), source, sink, "This regular expression is constructed from a $@.", source.getNode(), source.getNode().(Source).describe() diff --git a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected index 391be36fbb9..936d17028e3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-730/RegExpInjection.expected @@ -1,78 +1,3 @@ -nodes -| RegExpInjection.js:5:7:5:28 | key | -| RegExpInjection.js:5:13:5:28 | req.param("key") | -| RegExpInjection.js:5:13:5:28 | req.param("key") | -| RegExpInjection.js:5:31:5:56 | input | -| RegExpInjection.js:5:39:5:56 | req.param("input") | -| RegExpInjection.js:5:39:5:56 | req.param("input") | -| RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | -| RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | -| RegExpInjection.js:8:31:8:33 | key | -| RegExpInjection.js:19:14:19:22 | wrap(key) | -| RegExpInjection.js:19:14:19:22 | wrap(key) | -| RegExpInjection.js:19:19:19:21 | key | -| RegExpInjection.js:21:14:21:22 | wrap(key) | -| RegExpInjection.js:21:14:21:22 | wrap(key) | -| RegExpInjection.js:21:19:21:21 | key | -| RegExpInjection.js:24:12:24:27 | req.param("key") | -| RegExpInjection.js:24:12:24:27 | req.param("key") | -| RegExpInjection.js:27:14:27:21 | getKey() | -| RegExpInjection.js:27:14:27:21 | getKey() | -| RegExpInjection.js:29:21:29:21 | s | -| RegExpInjection.js:29:21:29:21 | s | -| RegExpInjection.js:31:23:31:23 | s | -| RegExpInjection.js:31:23:31:23 | s | -| RegExpInjection.js:31:23:31:23 | s | -| RegExpInjection.js:33:12:33:14 | key | -| RegExpInjection.js:34:12:34:19 | getKey() | -| RegExpInjection.js:40:23:40:27 | input | -| RegExpInjection.js:40:23:40:27 | input | -| RegExpInjection.js:41:26:41:30 | input | -| RegExpInjection.js:41:26:41:30 | input | -| RegExpInjection.js:42:25:42:29 | input | -| RegExpInjection.js:42:25:42:29 | input | -| RegExpInjection.js:45:24:45:28 | input | -| RegExpInjection.js:45:24:45:28 | input | -| RegExpInjection.js:46:27:46:31 | input | -| RegExpInjection.js:46:27:46:31 | input | -| RegExpInjection.js:47:26:47:30 | input | -| RegExpInjection.js:47:26:47:30 | input | -| RegExpInjection.js:54:14:54:16 | key | -| RegExpInjection.js:54:14:54:27 | key.split(".") | -| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | -| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | -| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | -| RegExpInjection.js:60:31:60:56 | input | -| RegExpInjection.js:60:39:60:56 | req.param("input") | -| RegExpInjection.js:60:39:60:56 | req.param("input") | -| RegExpInjection.js:64:14:64:18 | input | -| RegExpInjection.js:64:14:64:18 | input | -| RegExpInjection.js:82:7:82:32 | input | -| RegExpInjection.js:82:15:82:32 | req.param("input") | -| RegExpInjection.js:82:15:82:32 | req.param("input") | -| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | -| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | -| RegExpInjection.js:87:25:87:29 | input | -| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | -| RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | -| RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | -| RegExpInjection.js:91:20:91:30 | process.env | -| RegExpInjection.js:91:20:91:30 | process.env | -| RegExpInjection.js:91:20:91:35 | process.env.HOME | -| RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | -| RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | -| RegExpInjection.js:93:20:93:31 | process.argv | -| RegExpInjection.js:93:20:93:31 | process.argv | -| RegExpInjection.js:93:20:93:34 | process.argv[1] | -| tst.js:1:46:1:46 | e | -| tst.js:1:46:1:46 | e | -| tst.js:2:9:2:21 | data | -| tst.js:2:16:2:16 | e | -| tst.js:2:16:2:21 | e.data | -| tst.js:3:16:3:35 | "^"+ data.name + "$" | -| tst.js:3:16:3:35 | "^"+ data.name + "$" | -| tst.js:3:21:3:24 | data | -| tst.js:3:21:3:29 | data.name | edges | RegExpInjection.js:5:7:5:28 | key | RegExpInjection.js:8:31:8:33 | key | | RegExpInjection.js:5:7:5:28 | key | RegExpInjection.js:19:19:19:21 | key | @@ -80,69 +5,99 @@ edges | RegExpInjection.js:5:7:5:28 | key | RegExpInjection.js:33:12:33:14 | key | | RegExpInjection.js:5:7:5:28 | key | RegExpInjection.js:54:14:54:16 | key | | RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:5:7:5:28 | key | -| RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:5:7:5:28 | key | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:40:23:40:27 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:40:23:40:27 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:41:26:41:30 | input | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:41:26:41:30 | input | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:42:25:42:29 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:42:25:42:29 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:45:24:45:28 | input | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:45:24:45:28 | input | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:46:27:46:31 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:46:27:46:31 | input | | RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:47:26:47:30 | input | -| RegExpInjection.js:5:31:5:56 | input | RegExpInjection.js:47:26:47:30 | input | -| RegExpInjection.js:5:39:5:56 | req.param("input") | RegExpInjection.js:5:31:5:56 | input | | RegExpInjection.js:5:39:5:56 | req.param("input") | RegExpInjection.js:5:31:5:56 | input | | RegExpInjection.js:8:31:8:33 | key | RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | -| RegExpInjection.js:8:31:8:33 | key | RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | +| RegExpInjection.js:10:17:10:17 | s | RegExpInjection.js:11:26:11:26 | s | +| RegExpInjection.js:11:20:11:27 | wrap2(s) | RegExpInjection.js:11:12:11:27 | "\\\\b" + wrap2(s) | +| RegExpInjection.js:11:26:11:26 | s | RegExpInjection.js:11:20:11:27 | wrap2(s) | +| RegExpInjection.js:11:26:11:26 | s | RegExpInjection.js:14:18:14:18 | s | +| RegExpInjection.js:14:18:14:18 | s | RegExpInjection.js:15:12:15:12 | s | +| RegExpInjection.js:15:12:15:12 | s | RegExpInjection.js:15:12:15:24 | s + "=(.*)\\n" | +| RegExpInjection.js:19:19:19:21 | key | RegExpInjection.js:10:17:10:17 | s | | RegExpInjection.js:19:19:19:21 | key | RegExpInjection.js:19:14:19:22 | wrap(key) | -| RegExpInjection.js:19:19:19:21 | key | RegExpInjection.js:19:14:19:22 | wrap(key) | -| RegExpInjection.js:21:19:21:21 | key | RegExpInjection.js:21:14:21:22 | wrap(key) | +| RegExpInjection.js:21:19:21:21 | key | RegExpInjection.js:10:17:10:17 | s | | RegExpInjection.js:21:19:21:21 | key | RegExpInjection.js:21:14:21:22 | wrap(key) | | RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:27:14:27:21 | getKey() | -| RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:27:14:27:21 | getKey() | -| RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:27:14:27:21 | getKey() | -| RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:27:14:27:21 | getKey() | | RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:34:12:34:19 | getKey() | -| RegExpInjection.js:24:12:24:27 | req.param("key") | RegExpInjection.js:34:12:34:19 | getKey() | -| RegExpInjection.js:29:21:29:21 | s | RegExpInjection.js:31:23:31:23 | s | -| RegExpInjection.js:29:21:29:21 | s | RegExpInjection.js:31:23:31:23 | s | -| RegExpInjection.js:29:21:29:21 | s | RegExpInjection.js:31:23:31:23 | s | | RegExpInjection.js:29:21:29:21 | s | RegExpInjection.js:31:23:31:23 | s | | RegExpInjection.js:33:12:33:14 | key | RegExpInjection.js:29:21:29:21 | s | | RegExpInjection.js:34:12:34:19 | getKey() | RegExpInjection.js:29:21:29:21 | s | | RegExpInjection.js:54:14:54:16 | key | RegExpInjection.js:54:14:54:27 | key.split(".") | | RegExpInjection.js:54:14:54:27 | key.split(".") | RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | | RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | -| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | | RegExpInjection.js:60:31:60:56 | input | RegExpInjection.js:64:14:64:18 | input | -| RegExpInjection.js:60:31:60:56 | input | RegExpInjection.js:64:14:64:18 | input | -| RegExpInjection.js:60:39:60:56 | req.param("input") | RegExpInjection.js:60:31:60:56 | input | | RegExpInjection.js:60:39:60:56 | req.param("input") | RegExpInjection.js:60:31:60:56 | input | | RegExpInjection.js:82:7:82:32 | input | RegExpInjection.js:87:25:87:29 | input | | RegExpInjection.js:82:15:82:32 | req.param("input") | RegExpInjection.js:82:7:82:32 | input | -| RegExpInjection.js:82:15:82:32 | req.param("input") | RegExpInjection.js:82:7:82:32 | input | | RegExpInjection.js:87:25:87:29 | input | RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | | RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | -| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | -| RegExpInjection.js:91:20:91:30 | process.env | RegExpInjection.js:91:20:91:35 | process.env.HOME | -| RegExpInjection.js:91:20:91:30 | process.env | RegExpInjection.js:91:20:91:35 | process.env.HOME | -| RegExpInjection.js:91:20:91:35 | process.env.HOME | RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | -| RegExpInjection.js:91:20:91:35 | process.env.HOME | RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | -| RegExpInjection.js:93:20:93:31 | process.argv | RegExpInjection.js:93:20:93:34 | process.argv[1] | -| RegExpInjection.js:93:20:93:31 | process.argv | RegExpInjection.js:93:20:93:34 | process.argv[1] | -| RegExpInjection.js:93:20:93:34 | process.argv[1] | RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | -| RegExpInjection.js:93:20:93:34 | process.argv[1] | RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | -| tst.js:1:46:1:46 | e | tst.js:2:16:2:16 | e | +| RegExpInjection.js:91:20:91:30 | process.env | RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | +| RegExpInjection.js:93:20:93:31 | process.argv | RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | | tst.js:1:46:1:46 | e | tst.js:2:16:2:16 | e | | tst.js:2:9:2:21 | data | tst.js:3:21:3:24 | data | -| tst.js:2:16:2:16 | e | tst.js:2:16:2:21 | e.data | -| tst.js:2:16:2:21 | e.data | tst.js:2:9:2:21 | data | -| tst.js:3:21:3:24 | data | tst.js:3:21:3:29 | data.name | -| tst.js:3:21:3:29 | data.name | tst.js:3:16:3:35 | "^"+ data.name + "$" | -| tst.js:3:21:3:29 | data.name | tst.js:3:16:3:35 | "^"+ data.name + "$" | +| tst.js:2:16:2:16 | e | tst.js:2:9:2:21 | data | +| tst.js:3:21:3:24 | data | tst.js:3:16:3:35 | "^"+ data.name + "$" | +nodes +| RegExpInjection.js:5:7:5:28 | key | semmle.label | key | +| RegExpInjection.js:5:13:5:28 | req.param("key") | semmle.label | req.param("key") | +| RegExpInjection.js:5:31:5:56 | input | semmle.label | input | +| RegExpInjection.js:5:39:5:56 | req.param("input") | semmle.label | req.param("input") | +| RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | semmle.label | "\\\\b" + ... (.*)\\n" | +| RegExpInjection.js:8:31:8:33 | key | semmle.label | key | +| RegExpInjection.js:10:17:10:17 | s | semmle.label | s | +| RegExpInjection.js:11:12:11:27 | "\\\\b" + wrap2(s) | semmle.label | "\\\\b" + wrap2(s) | +| RegExpInjection.js:11:20:11:27 | wrap2(s) | semmle.label | wrap2(s) | +| RegExpInjection.js:11:26:11:26 | s | semmle.label | s | +| RegExpInjection.js:14:18:14:18 | s | semmle.label | s | +| RegExpInjection.js:15:12:15:12 | s | semmle.label | s | +| RegExpInjection.js:15:12:15:24 | s + "=(.*)\\n" | semmle.label | s + "=(.*)\\n" | +| RegExpInjection.js:19:14:19:22 | wrap(key) | semmle.label | wrap(key) | +| RegExpInjection.js:19:19:19:21 | key | semmle.label | key | +| RegExpInjection.js:21:14:21:22 | wrap(key) | semmle.label | wrap(key) | +| RegExpInjection.js:21:19:21:21 | key | semmle.label | key | +| RegExpInjection.js:24:12:24:27 | req.param("key") | semmle.label | req.param("key") | +| RegExpInjection.js:27:14:27:21 | getKey() | semmle.label | getKey() | +| RegExpInjection.js:29:21:29:21 | s | semmle.label | s | +| RegExpInjection.js:31:23:31:23 | s | semmle.label | s | +| RegExpInjection.js:33:12:33:14 | key | semmle.label | key | +| RegExpInjection.js:34:12:34:19 | getKey() | semmle.label | getKey() | +| RegExpInjection.js:40:23:40:27 | input | semmle.label | input | +| RegExpInjection.js:41:26:41:30 | input | semmle.label | input | +| RegExpInjection.js:42:25:42:29 | input | semmle.label | input | +| RegExpInjection.js:45:24:45:28 | input | semmle.label | input | +| RegExpInjection.js:46:27:46:31 | input | semmle.label | input | +| RegExpInjection.js:47:26:47:30 | input | semmle.label | input | +| RegExpInjection.js:54:14:54:16 | key | semmle.label | key | +| RegExpInjection.js:54:14:54:27 | key.split(".") | semmle.label | key.split(".") | +| RegExpInjection.js:54:14:54:42 | key.spl ... x => x) | semmle.label | key.spl ... x => x) | +| RegExpInjection.js:54:14:54:52 | key.spl ... in("-") | semmle.label | key.spl ... in("-") | +| RegExpInjection.js:60:31:60:56 | input | semmle.label | input | +| RegExpInjection.js:60:39:60:56 | req.param("input") | semmle.label | req.param("input") | +| RegExpInjection.js:64:14:64:18 | input | semmle.label | input | +| RegExpInjection.js:82:7:82:32 | input | semmle.label | input | +| RegExpInjection.js:82:15:82:32 | req.param("input") | semmle.label | req.param("input") | +| RegExpInjection.js:87:14:87:55 | "^.*\\.( ... + ")$" | semmle.label | "^.*\\.( ... + ")$" | +| RegExpInjection.js:87:25:87:29 | input | semmle.label | input | +| RegExpInjection.js:87:25:87:48 | input.r ... g, "\|") | semmle.label | input.r ... g, "\|") | +| RegExpInjection.js:91:16:91:50 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | +| RegExpInjection.js:91:20:91:30 | process.env | semmle.label | process.env | +| RegExpInjection.js:93:16:93:49 | `^${pro ... r.app$` | semmle.label | `^${pro ... r.app$` | +| RegExpInjection.js:93:20:93:31 | process.argv | semmle.label | process.argv | +| tst.js:1:46:1:46 | e | semmle.label | e | +| tst.js:2:9:2:21 | data | semmle.label | data | +| tst.js:2:16:2:16 | e | semmle.label | e | +| tst.js:3:16:3:35 | "^"+ data.name + "$" | semmle.label | "^"+ data.name + "$" | +| tst.js:3:21:3:24 | data | semmle.label | data | +subpaths +| RegExpInjection.js:11:26:11:26 | s | RegExpInjection.js:14:18:14:18 | s | RegExpInjection.js:15:12:15:24 | s + "=(.*)\\n" | RegExpInjection.js:11:20:11:27 | wrap2(s) | +| RegExpInjection.js:19:19:19:21 | key | RegExpInjection.js:10:17:10:17 | s | RegExpInjection.js:11:12:11:27 | "\\\\b" + wrap2(s) | RegExpInjection.js:19:14:19:22 | wrap(key) | +| RegExpInjection.js:21:19:21:21 | key | RegExpInjection.js:10:17:10:17 | s | RegExpInjection.js:11:12:11:27 | "\\\\b" + wrap2(s) | RegExpInjection.js:21:14:21:22 | wrap(key) | #select | RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:8:23:8:45 | "\\\\b" + ... (.*)\\n" | This regular expression is constructed from a $@. | RegExpInjection.js:5:13:5:28 | req.param("key") | user-provided value | | RegExpInjection.js:19:14:19:22 | wrap(key) | RegExpInjection.js:5:13:5:28 | req.param("key") | RegExpInjection.js:19:14:19:22 | wrap(key) | This regular expression is constructed from a $@. | RegExpInjection.js:5:13:5:28 | req.param("key") | user-provided value |