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/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll index 4f4bc2782ab..f64078c7c54 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll @@ -10,7 +10,8 @@ newtype TEndpointType = TXssSinkType() or TNosqlInjectionSinkType() or TSqlInjectionSinkType() or - TTaintedPathSinkType() + TTaintedPathSinkType() or + TCodeInjectionSinkType() /** A class that can be predicted by endpoint scoring models. */ abstract class EndpointType extends TEndpointType { @@ -55,3 +56,10 @@ class TaintedPathSinkType extends EndpointType, TTaintedPathSinkType { override int getEncoding() { result = 4 } } + +/** The `CodeInjectionSink` class that can be predicted by endpoint scoring models. */ +class CodeInjectionSinkType extends EndpointType, TCodeInjectionSinkType { + override string getDescription() { result = "CodeInjectionSink" } + + override int getEncoding() { result = 5 } +} 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