From e5bfb94403e38140c244e25bb45bab098ed5320f Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Fri, 28 Jan 2022 13:39:58 +0100 Subject: [PATCH] Boost StoredXss and XssThroughDomATM Produced with: ``` javascript/ql$tb boost src/Security/CWE-079/StoredXss.ql XssSink javascript/ql$ tb boost src/Security/CWE-079/XssThroughDom.ql XssSink ``` --- .../adaptivethreatmodeling/StoredXssATM.qll | 72 +++++++++++++++++++ .../XssThroughDomATM.qll | 72 +++++++++++++++++++ .../src/StoredXssATM.ql | 29 ++++++++ .../src/XssThroughDomATM.ql | 29 ++++++++ 4 files changed, 202 insertions(+) create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StoredXssATM.qll create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/src/StoredXssATM.ql create mode 100644 javascript/ql/experimental/adaptivethreatmodeling/src/XssThroughDomATM.ql diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StoredXssATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StoredXssATM.qll new file mode 100644 index 00000000000..6f82279557c --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StoredXssATM.qll @@ -0,0 +1,72 @@ +/** + * Provides a taint-tracking configuration for reasoning about stored + * cross-site scripting vulnerabilities. + * Is boosted by ATM. + */ + +import javascript +import AdaptiveThreatModeling +import semmle.javascript.security.dataflow.Xss::StoredXss + +/** + * 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 StoredXssATMConfig extends ATMConfig { + StoredXssATMConfig() { this = "StoredXssATMConfig" } + + 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 XssSinkType } +} + +/** + * A taint-tracking configuration for reasoning about XSS. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "StoredXssATMConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { + (sink instanceof Sink or any(StoredXssATMConfig cfg).isEffectiveSink(sink)) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof Sanitizer + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof SanitizerGuard + } +} + +/** A file name, considered as a flow source for stored XSS. */ +class FileNameSourceAsSource extends Source { + FileNameSourceAsSource() { this instanceof FileNameSource } +} + +/** An instance of user-controlled torrent information, considered as a flow source for stored XSS. */ +class UserControlledTorrentInfoAsSource extends Source { + UserControlledTorrentInfoAsSource() { this instanceof ParseTorrent::UserControlledTorrentInfo } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll new file mode 100644 index 00000000000..38c5ba45a8f --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll @@ -0,0 +1,72 @@ +/** + * Provides a taint-tracking configuration for reasoning about + * cross-site scripting vulnerabilities through the DOM. + * Is boosted by ATM. + */ + +import javascript +import AdaptiveThreatModeling +private import semmle.javascript.dataflow.InferredTypes +import semmle.javascript.security.dataflow.Xss::XssThroughDom +private import semmle.javascript.security.dataflow.XssThroughDomCustomizations::XssThroughDom +private import semmle.javascript.security.dataflow.Xss::DomBasedXss as DomBasedXss +private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQuery + +/** + * 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 XssThroughDOMATMConfig extends ATMConfig { + XssThroughDOMATMConfig() { this = "XssThroughDOMATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { source instanceof Source } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof DomBasedXss::Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof XssSinkType } +} + +/** + * A taint-tracking configuration for reasoning about XSS through the DOM. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "XssThroughDOMATMConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { + (sink instanceof DomBasedXss::Sink or any(XssThroughDOMATMConfig cfg).isEffectiveSink(sink)) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof DomBasedXss::Sanitizer + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof TypeTestGuard or + guard instanceof UnsafeJQuery::PropertyPresenceSanitizer or + guard instanceof DomBasedXss::SanitizerGuard + } + + override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { + DomBasedXss::isOptionallySanitizedEdge(pred, succ) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/StoredXssATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/StoredXssATM.ql new file mode 100644 index 00000000000..5941191d4cd --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/StoredXssATM.ql @@ -0,0 +1,29 @@ +/** + * For internal use only. + * + * @name Stored cross-site scripting (boosted) + * @description Using uncontrolled stored values in HTML allows for a stored cross-site scripting vulnerability. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 6.1 + * @id adaptive-threat-modeling/js/stored-xss + * @tags experimental experimental/atm security external/cwe/cwe-079 external/cwe/cwe-116 + */ + +import experimental.adaptivethreatmodeling.StoredXssATM +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/stored-xss result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/XssThroughDomATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/XssThroughDomATM.ql new file mode 100644 index 00000000000..8ccf78bcd07 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/XssThroughDomATM.ql @@ -0,0 +1,29 @@ +/** + * For internal use only. + * + * @name DOM text reinterpreted as HTML (boosted) + * @description Reinterpreting text from the DOM as HTML can lead to a cross-site scripting vulnerability. + * @kind path-problem + * @scored + * @problem.severity warning + * @security-severity 6.1 + * @id adaptive-threat-modeling/js/xss-through-dom + * @tags experimental experimental/atm security external/cwe/cwe-079 external/cwe/cwe-116 + */ + +import experimental.adaptivethreatmodeling.XssThroughDomATM +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/xss-through-dom result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score