mirror of
https://github.com/github/codeql.git
synced 2026-01-28 05:42:58 +01:00
85 lines
3.0 KiB
Plaintext
85 lines
3.0 KiB
Plaintext
/**
|
|
* Provides a taint-tracking configuration for reasoning about DOM-based
|
|
* cross-site scripting vulnerabilities in unsafe jQuery plugins.
|
|
*/
|
|
|
|
import javascript
|
|
import semmle.javascript.security.dataflow.Xss
|
|
import UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin
|
|
|
|
/**
|
|
* A taint-tracking configuration for reasoning about XSS in unsafe jQuery plugins.
|
|
*/
|
|
class Configuration extends TaintTracking::Configuration {
|
|
Configuration() { this = "UnsafeJQueryPlugin" }
|
|
|
|
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
|
|
|
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
|
|
|
override predicate isSanitizer(DataFlow::Node node) {
|
|
super.isSanitizer(node)
|
|
or
|
|
node instanceof DomBasedXss::Sanitizer
|
|
or
|
|
node instanceof Sanitizer
|
|
}
|
|
|
|
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
|
// jQuery plugins tend to be implemented as classes that store data in fields initialized by the constructor.
|
|
DataFlow::localFieldStep(src, sink) or
|
|
aliasPropertyPresenceStep(src, sink)
|
|
}
|
|
|
|
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
|
|
// prefixing prevents forced html/css confusion:
|
|
// prefixing through concatenation:
|
|
StringConcatenation::taintStep(pred, succ, _, any(int i | i >= 1))
|
|
or
|
|
// prefixing through a poor-mans templating system:
|
|
exists(StringReplaceCall replace |
|
|
replace = succ and
|
|
pred = replace.getRawReplacement()
|
|
)
|
|
}
|
|
|
|
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
|
|
super.isSanitizerGuard(node) or
|
|
node instanceof IsElementSanitizer or
|
|
node instanceof PropertyPresenceSanitizer or
|
|
node instanceof NumberGuard
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds if there is a taint-step from `src` to `sink`,
|
|
* where `src` is a property read that acts as a sanitizer for the base,
|
|
* and `sink` is that same property read from the same base.
|
|
*
|
|
* For an condition like `if(foo.bar) {...}`, the base `foo` is sanitized but the property `foo.bar` is not.
|
|
* With this taint-step we regain that `foo.bar` is tainted, because `PropertyPresenceSanitizer` could remove it.
|
|
*/
|
|
private predicate aliasPropertyPresenceStep(DataFlow::Node src, DataFlow::Node sink) {
|
|
exists(ReachableBasicBlock srcBB, ReachableBasicBlock sinkBB |
|
|
aliasPropertyPresenceStepHelper(src, sink, srcBB, sinkBB) and
|
|
srcBB.strictlyDominates(sinkBB)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if there is a taint-step from `src` to `sink`, and `srcBB` is the basicblock for `src` and `sinkBB` is the basicblock for `sink`.
|
|
*
|
|
* This predicate is outlined to get a better join-order.
|
|
*/
|
|
pragma[noinline]
|
|
private predicate aliasPropertyPresenceStepHelper(
|
|
DataFlow::PropRead src, DataFlow::Node sink, ReachableBasicBlock srcBB, ReachableBasicBlock sinkBB
|
|
) {
|
|
exists(PropertyPresenceSanitizer sanitizer |
|
|
src = sanitizer.getPropRead() and
|
|
sink = AccessPath::getAnAliasedSourceNode(src) and
|
|
srcBB = src.getBasicBlock() and
|
|
sinkBB = sink.getBasicBlock()
|
|
)
|
|
}
|