From 4af76943098cafa146414806cf15beefb444822b Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 5 Oct 2023 09:22:04 +0200 Subject: [PATCH] JS: Port ResourceExhaustion --- .../ResourceExhaustionCustomizations.qll | 15 +++ .../dataflow/ResourceExhaustionQuery.qll | 30 ++++- .../Security/CWE-770/ResourceExhaustion.ql | 6 +- .../ResourceExhaustion.expected | 119 ++++++------------ 4 files changed, 80 insertions(+), 90 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll index 8307c1f6f93..a26d4a2e9a5 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionCustomizations.qll @@ -31,6 +31,21 @@ module ResourceExhaustion { */ abstract class Sanitizer extends DataFlow::Node { } + /** + * A barrier guard for resource exhaustion vulnerabilities. + */ + abstract class BarrierGuard extends DataFlow::Node { + /** + * Holds if this node acts as a barrier for data flow, blocking further flow from `e` if `this` evaluates to `outcome`. + */ + predicate blocksExpr(boolean outcome, Expr e) { none() } + } + + /** A subclass of `BarrierGuard` that is used for backward compatibility with the old data flow library. */ + abstract class BarrierGuardLegacy extends BarrierGuard, TaintTracking::SanitizerGuardNode { + override predicate sanitizes(boolean outcome, Expr e) { this.blocksExpr(outcome, e) } + } + /** A source of remote user input, considered as a data flow source for resource exhaustion vulnerabilities. */ class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource { RemoteFlowSourceAsSource() { diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionQuery.qll index 366d1db6973..01cab949741 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ResourceExhaustionQuery.qll @@ -13,7 +13,31 @@ import ResourceExhaustionCustomizations::ResourceExhaustion /** * A data flow configuration for resource exhaustion vulnerabilities. */ -class Configuration extends TaintTracking::Configuration { +module ResourceExhaustionConfig 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 or + node = any(DataFlow::PropRead read | read.getPropertyName() = "length") or + node = DataFlow::MakeBarrierGuard::getABarrierNode() + } + + predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst) { + isNumericFlowStep(src, dst) + } +} + +/** + * Data flow for resource exhaustion vulnerabilities. + */ +module ResourceExhaustionFlow = TaintTracking::Global; + +/** + * DEPRECATED. Use the `ResourceExhaustionFlow` module instead. + */ +deprecated class Configuration extends TaintTracking::Configuration { Configuration() { this = "ResourceExhaustion" } override predicate isSource(DataFlow::Node source) { source instanceof Source } @@ -49,10 +73,10 @@ predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) { /** * A sanitizer that blocks taint flow if the size of a number is limited. */ -class UpperBoundsCheckSanitizerGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode { +class UpperBoundsCheckSanitizerGuard extends BarrierGuardLegacy, DataFlow::ValueNode { override RelationalComparison astNode; - override predicate sanitizes(boolean outcome, Expr e) { + override predicate blocksExpr(boolean outcome, Expr e) { true = outcome and e = astNode.getLesserOperand() or diff --git a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql index 4a32424ac3e..89452bea8ca 100644 --- a/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql +++ b/javascript/ql/src/Security/CWE-770/ResourceExhaustion.ql @@ -13,10 +13,10 @@ */ import javascript -import DataFlow::PathGraph import semmle.javascript.security.dataflow.ResourceExhaustionQuery +import ResourceExhaustionFlow::PathGraph -from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink -where dataflow.hasFlowPath(source, sink) +from ResourceExhaustionFlow::PathNode source, ResourceExhaustionFlow::PathNode sink +where ResourceExhaustionFlow::flowPath(source, sink) select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from a $@.", source, "user-provided value" diff --git a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion/ResourceExhaustion.expected b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion/ResourceExhaustion.expected index 1c8a7172c6d..f3b767c64b4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion/ResourceExhaustion.expected +++ b/javascript/ql/test/query-tests/Security/CWE-770/ResourceExhaustion/ResourceExhaustion.expected @@ -1,115 +1,66 @@ -nodes -| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | -| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | -| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | -| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | -| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | -| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | -| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | -| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| resource-exhaustion.js:5:7:5:42 | s | -| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | -| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | -| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | -| resource-exhaustion.js:5:21:5:27 | req.url | -| resource-exhaustion.js:5:21:5:27 | req.url | -| resource-exhaustion.js:6:7:6:21 | n | -| resource-exhaustion.js:6:11:6:21 | parseInt(s) | -| resource-exhaustion.js:6:20:6:20 | s | -| resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:15:22:15:22 | n | -| resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:20:20:20:20 | n | -| resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:27:9:27:9 | n | -| resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:29:9:29:9 | n | -| resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:31:9:31:9 | n | -| resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:34:12:34:12 | n | -| resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:82:17:82:17 | s | -| resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:83:18:83:18 | n | -| resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:88:16:88:16 | n | -| resource-exhaustion.js:88:16:88:16 | n | -| resource-exhaustion.js:92:18:92:18 | n | -| resource-exhaustion.js:92:18:92:18 | n | edges | documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | -| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | | documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | -| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | -| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | +| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | -| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | | resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:6:20:6:20 | s | | resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | | resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s | | resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s | -| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | resource-exhaustion.js:5:11:5:40 | url.par ... ).query | -| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | resource-exhaustion.js:5:11:5:42 | url.par ... query.s | -| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | resource-exhaustion.js:5:7:5:42 | s | -| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | +| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | resource-exhaustion.js:5:7:5:42 | s | | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:88:16:88:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:88:16:88:16 | n | -| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:92:18:92:18 | n | | resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:92:18:92:18 | n | | resource-exhaustion.js:6:11:6:21 | parseInt(s) | resource-exhaustion.js:6:7:6:21 | n | | resource-exhaustion.js:6:20:6:20 | s | resource-exhaustion.js:6:11:6:21 | parseInt(s) | +nodes +| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | semmle.label | delay | +| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | semmle.label | parseIn ... .delay) | +| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | semmle.label | url.par ... , true) | +| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | semmle.label | url.par ... y.delay | +| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | semmle.label | req.url | +| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | semmle.label | delay | +| resource-exhaustion.js:5:7:5:42 | s | semmle.label | s | +| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | semmle.label | url.par ... , true) | +| resource-exhaustion.js:5:21:5:27 | req.url | semmle.label | req.url | +| resource-exhaustion.js:6:7:6:21 | n | semmle.label | n | +| resource-exhaustion.js:6:11:6:21 | parseInt(s) | semmle.label | parseInt(s) | +| resource-exhaustion.js:6:20:6:20 | s | semmle.label | s | +| resource-exhaustion.js:14:16:14:16 | n | semmle.label | n | +| resource-exhaustion.js:15:22:15:22 | n | semmle.label | n | +| resource-exhaustion.js:16:26:16:26 | n | semmle.label | n | +| resource-exhaustion.js:20:20:20:20 | n | semmle.label | n | +| resource-exhaustion.js:22:18:22:18 | n | semmle.label | n | +| resource-exhaustion.js:27:9:27:9 | n | semmle.label | n | +| resource-exhaustion.js:28:13:28:13 | n | semmle.label | n | +| resource-exhaustion.js:29:9:29:9 | n | semmle.label | n | +| resource-exhaustion.js:30:9:30:9 | n | semmle.label | n | +| resource-exhaustion.js:31:9:31:9 | n | semmle.label | n | +| resource-exhaustion.js:32:9:32:9 | n | semmle.label | n | +| resource-exhaustion.js:34:12:34:12 | n | semmle.label | n | +| resource-exhaustion.js:35:12:35:12 | s | semmle.label | s | +| resource-exhaustion.js:81:17:81:17 | n | semmle.label | n | +| resource-exhaustion.js:82:17:82:17 | s | semmle.label | s | +| resource-exhaustion.js:83:18:83:18 | n | semmle.label | n | +| resource-exhaustion.js:84:18:84:18 | s | semmle.label | s | +| resource-exhaustion.js:88:16:88:16 | n | semmle.label | n | +| resource-exhaustion.js:92:18:92:18 | n | semmle.label | n | +subpaths #select | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | This creates a timer with a user-controlled duration from a $@. | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | user-provided value | | resource-exhaustion.js:14:16:14:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:14:16:14:16 | n | This creates a buffer with a user-controlled size from a $@. | resource-exhaustion.js:5:21:5:27 | req.url | user-provided value |