From de1bc89099fb7b8a281ccf54f630c81ac2fc9daf Mon Sep 17 00:00:00 2001 From: tombolton Date: Thu, 12 May 2022 11:04:31 +0100 Subject: [PATCH] add CodeInjection extraction and evaluation queries --- .../CodeInjectionATM.qll | 67 +++++++++++++++++++ .../modelbuilding/evaluation/CodeInjection.ql | 26 +++++++ .../evaluation/CodeInjectionATM.ql | 28 ++++++++ .../evaluation/CodeInjectionATMLite.ql | 30 +++++++++ .../src/CodeInjectionATM.ql | 29 ++++++++ 5 files changed, 180 insertions(+) create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeInjectionATM.qll create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjection.ql create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATM.ql create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATMLite.ql create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/src/CodeInjectionATM.ql diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeInjectionATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeInjectionATM.qll new file mode 100644 index 00000000000..222c2963f7c --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeInjectionATM.qll @@ -0,0 +1,67 @@ +/** + * Provides a taint-tracking configuration for reasoning about code + * injection vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `CodeInjection::Configuration` is needed, otherwise + * `CodeInjectionCustomizations` should be imported instead. + * Is boosted by ATM. + */ + +import javascript +import AdaptiveThreatModeling +import semmle.javascript.security.dataflow.CodeInjectionCustomizations::CodeInjection + +/** + * This module provides logic to filter candidate sinks to those which are likely XSS sinks. + */ +module SinkEndpointFilter { + private import StandardEndpointFilters as StandardEndpointFilters + + /** + * Provides a set of reasons why a given data flow node should be excluded as a sink candidate. + * + * If this predicate has no results for a sink candidate `n`, then we should treat `n` as an + * effective sink. + */ + string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) { + result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate) + } +} + +class CodeInjectionATMConfig extends ATMConfig { + CodeInjectionATMConfig() { this = "CodeInjectionATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { source instanceof Source } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof CodeInjectionSinkType } +} + +/** + * A taint-tracking configuration for reasoning about code injection vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "CodeInjectionATMConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { + (sink instanceof Sink or any(CodeInjectionATMConfig cfg).isEffectiveSink(sink)) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof Sanitizer + } + + override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node trg) { + // HTML sanitizers are insufficient protection against code injection + src = trg.(HtmlSanitizerCall).getInput() + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjection.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjection.ql new file mode 100644 index 00000000000..9467bb0e263 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjection.ql @@ -0,0 +1,26 @@ +/** + * CodeInjection.ql + * + * Version of the standard Code Injection query with an output relation ready to plug into the evaluation + * pipeline. + * + * Standard query: javascript/ql/src/Security/CWE-094/CodeInjection.ql + */ + +import semmle.javascript.security.dataflow.CodeInjectionQuery +import EndToEndEvaluation as EndToEndEvaluation + +from + DataFlow::Configuration cfg, DataFlow::Node source, DataFlow::Node sink, string filePathSink, + int startLineSink, int endLineSink, int startColumnSink, int endColumnSink, string filePathSource, + int startLineSource, int endLineSource, int startColumnSource, int endColumnSource +where + cfg instanceof Configuration and + cfg.hasFlow(source, sink) and + not EndToEndEvaluation::isFlowExcluded(source, sink) and + sink.hasLocationInfo(filePathSink, startLineSink, startColumnSink, endLineSink, endColumnSink) and + source + .hasLocationInfo(filePathSource, startLineSource, startColumnSource, endLineSource, + endColumnSource) +select source, startLineSource, startColumnSource, endLineSource, endColumnSource, filePathSource, + sink, startLineSink, startColumnSink, endLineSink, endColumnSink, filePathSink diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATM.ql new file mode 100644 index 00000000000..f5839a9d7e5 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATM.ql @@ -0,0 +1,28 @@ +/** + * CodeInjectionATM.ql + * + * Version of the boosted Code Injection query with an output relation ready to plug into the evaluation + * pipeline. + */ + +import ATM::ResultsInfo +import EndToEndEvaluation as EndToEndEvaluation +import experimental.adaptivethreatmodeling.CodeInjectionATM + +from + DataFlow::Configuration cfg, DataFlow::Node source, DataFlow::Node sink, string filePathSink, + int startLineSink, int endLineSink, int startColumnSink, int endColumnSink, string filePathSource, + int startLineSource, int endLineSource, int startColumnSource, int endColumnSource, float score +where + cfg.hasFlow(source, sink) and + not EndToEndEvaluation::isFlowExcluded(source, sink) and + not isFlowLikelyInBaseQuery(source, sink) and + sink.hasLocationInfo(filePathSink, startLineSink, startColumnSink, endLineSink, endColumnSink) and + source + .hasLocationInfo(filePathSource, startLineSource, startColumnSource, endLineSource, + endColumnSource) and + getScoreForFlow(source, sink) = score +select source, startLineSource, startColumnSource, endLineSource, endColumnSource, filePathSource, + sink, startLineSink, startColumnSink, endLineSink, endColumnSink, filePathSink, score order by + score desc, startLineSource, startColumnSource, endLineSource, endColumnSource, filePathSource, + startLineSink, startColumnSink, endLineSink, endColumnSink, filePathSink diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATMLite.ql b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATMLite.ql new file mode 100644 index 00000000000..995bfd640a2 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/evaluation/CodeInjectionATMLite.ql @@ -0,0 +1,30 @@ +/** + * CodeInjectionATMLite.ql + * + * Arbitrarily ranked version of the boosted XSS query with an output relation ready to plug into + * the evaluation pipeline. This is useful (a) for evaluating the performance of endpoint filters, + * and (b) as a baseline to compare the model against. + */ + +import javascript +import ATM::ResultsInfo +import EndToEndEvaluation as EndToEndEvaluation +import experimental.adaptivethreatmodeling.CodeInjectionATM + +from + DataFlow::Configuration cfg, DataFlow::Node source, DataFlow::Node sink, string filePathSink, + int startLineSink, int endLineSink, int startColumnSink, int endColumnSink, string filePathSource, + int startLineSource, int endLineSource, int startColumnSource, int endColumnSource, float score +where + cfg.hasFlow(source, sink) and + not EndToEndEvaluation::isFlowExcluded(source, sink) and + not isFlowLikelyInBaseQuery(source, sink) and + sink.hasLocationInfo(filePathSink, startLineSink, startColumnSink, endLineSink, endColumnSink) and + source + .hasLocationInfo(filePathSource, startLineSource, startColumnSource, endLineSource, + endColumnSource) and + score = 0 +select source, startLineSource, startColumnSource, endLineSource, endColumnSource, filePathSource, + sink, startLineSink, startColumnSink, endLineSink, endColumnSink, filePathSink, score order by + score desc, startLineSource, startColumnSource, endLineSource, endColumnSource, filePathSource, + startLineSink, startColumnSink, endLineSink, endColumnSink, filePathSink diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/CodeInjectionATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/CodeInjectionATM.ql new file mode 100644 index 00000000000..f6a172ae6d9 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/CodeInjectionATM.ql @@ -0,0 +1,29 @@ +/** + * For internal use only. + * + * @name Code injection (boosted) + * @description Interpreting unsanitized user input as code allows a malicious user arbitrary code execution. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 9.3 + * @id adaptive-threat-modeling/js/code-injection + * @tags experimental experimental/atm security external/cwe/cwe-094 external/cwe/cwe-095 external/cwe/cwe-079 external/cwe/cwe-116 + */ + +import experimental.adaptivethreatmodeling.CodeInjectionATM +import ATM::ResultsInfo +import DataFlow::PathGraph + +from + DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score, + string scoreString +where + cfg.hasFlowPath(source, sink) and + not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and + score = getScoreForFlow(source.getNode(), sink.getNode()) and + scoreString = getScoreStringForFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, + "[Score = " + scoreString + "] This may be a js/code-injection result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score