JS: Port UnsafeJQueryPlugin

This commit is contained in:
Asger F
2023-10-05 09:25:48 +02:00
parent 6e3f4bd7d8
commit d08e4504ff
4 changed files with 136 additions and 215 deletions

View File

@@ -31,6 +31,21 @@ module UnsafeJQueryPlugin {
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A barrier guard for XSS in unsafe jQuery plugins.
*/
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) }
}
/**
* The receiver of a function, seen as a sanitizer.
*
@@ -110,7 +125,7 @@ module UnsafeJQueryPlugin {
/**
* An expression of form `isElement(x)`, which sanitizes `x`.
*/
class IsElementSanitizer extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
class IsElementSanitizer extends BarrierGuardLegacy, DataFlow::CallNode {
IsElementSanitizer() {
// common ad hoc sanitizing calls
exists(string name | this.getCalleeName() = name |
@@ -118,7 +133,7 @@ module UnsafeJQueryPlugin {
)
}
override predicate sanitizes(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
outcome = true and e = this.getArgument(0).asExpr()
}
}
@@ -126,7 +141,7 @@ module UnsafeJQueryPlugin {
/**
* An expression like `typeof x.<?> !== "undefined"` or `x.<?>`, which sanitizes `x`, as it is unlikely to be a string afterwards.
*/
class PropertyPresenceSanitizer extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
class PropertyPresenceSanitizer extends BarrierGuardLegacy, DataFlow::ValueNode {
DataFlow::Node input;
boolean polarity;
@@ -155,20 +170,20 @@ module UnsafeJQueryPlugin {
*/
DataFlow::PropRead getPropRead() { result = this }
override predicate sanitizes(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
outcome = polarity and
e = input.asExpr()
}
}
/** A guard that checks whether `x` is a number. */
class NumberGuard extends TaintTracking::SanitizerGuardNode instanceof DataFlow::CallNode {
class NumberGuard extends BarrierGuardLegacy instanceof DataFlow::CallNode {
Expr x;
boolean polarity;
NumberGuard() { TaintTracking::isNumberGuard(this, x, polarity) }
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
override predicate blocksExpr(boolean outcome, Expr e) { e = x and outcome = polarity }
}
/**

View File

@@ -10,7 +10,46 @@ import UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin
/**
* A taint-tracking configuration for reasoning about XSS in unsafe jQuery plugins.
*/
class Configuration extends TaintTracking::Configuration {
module UnsafeJQueryPluginConfig implements DataFlow::ConfigSig {
// TODO: PropertyPresenceSanitizer should not block values in a content.
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) {
node instanceof DomBasedXss::Sanitizer or
node instanceof Sanitizer or
node = DataFlow::MakeBarrierGuard<BarrierGuard>::getABarrierNode()
}
predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node sink) {
// jQuery plugins tend to be implemented as classes that store data in fields initialized by the constructor.
// TODO: localFieldStep is too expensive with dataflow2
// DataFlow::localFieldStep(pred, succ)
none()
or
aliasPropertyPresenceStep(src, sink)
}
predicate isBarrierOut(DataFlow::Node node) {
// prefixing prevents forced html/css confusion:
// prefixing through concatenation:
StringConcatenation::taintStep(node, _, _, any(int i | i >= 1))
or
// prefixing through a poor-mans templating system:
node = any(StringReplaceCall call).getRawReplacement()
}
}
/**
* Taint-tracking for reasoning about XSS in unsafe jQuery plugins.
*/
module UnsafeJQueryPluginFlow = TaintTracking::Global<UnsafeJQueryPluginConfig>;
/**
* DEPRECATED. Use the `UnsafeJQueryPluginFlow` module instead.
*/
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "UnsafeJQueryPlugin" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }