JS: Migrate UnvalidatedDynamicMethodCall

This commit is contained in:
Asger F
2024-12-13 11:47:25 +01:00
parent 820f81fc10
commit c951a29e2a
2 changed files with 87 additions and 39 deletions

View File

@@ -10,16 +10,60 @@ import PropertyInjectionShared
private import semmle.javascript.dataflow.InferredTypes
module UnvalidatedDynamicMethodCall {
private import DataFlow::FlowLabel
private newtype TFlowState =
TTaint() or
TMaybeNonFunction() or
TMaybeFromProto()
/** A flow state to associate with a tracked value. */
class FlowState extends TFlowState {
/** Gets a string representation fo this flow state */
string toString() {
this = TTaint() and result = "taint"
or
this = TMaybeNonFunction() and result = "maybe-non-function"
or
this = TMaybeFromProto() and result = "maybe-from-proto"
}
deprecated DataFlow::FlowLabel toFlowLabel() {
this = TTaint() and result.isTaint()
or
this = TMaybeNonFunction() and result instanceof MaybeNonFunction
or
this = TMaybeFromProto() and result instanceof MaybeFromProto
}
}
/** Predicates for working with flow states. */
module FlowState {
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
/** A tainted value. */
FlowState taint() { result = TTaint() }
/**
* A non-function value, obtained by reading from a tainted property name.
*/
FlowState maybeNonFunction() { result = TMaybeNonFunction() }
/**
* A value obtained from a prototype object while reading from a tainted property name.
*/
FlowState maybeFromProto() { result = TMaybeFromProto() }
}
/**
* A data flow source for unvalidated dynamic method calls.
*/
abstract class Source extends DataFlow::Node {
/**
* Gets the flow label relevant for this source.
* Gets the flow state relevant for this source.
*/
DataFlow::FlowLabel getFlowLabel() { result = taint() }
FlowState getAFlowState() { result = FlowState::taint() }
/** DEPRECATED. Use `getAFlowState()` instead. */
deprecated DataFlow::FlowLabel getFlowLabel() { result = this.getAFlowState().toFlowLabel() }
}
/**
@@ -27,9 +71,12 @@ module UnvalidatedDynamicMethodCall {
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets the flow label relevant for this sink
* Gets the flow state relevant for this sink
*/
abstract DataFlow::FlowLabel getFlowLabel();
FlowState getAFlowState() { result = FlowState::taint() }
/** DEPRECATED. Use `getAFlowState()` instead. */
deprecated DataFlow::FlowLabel getFlowLabel() { result = this.getAFlowState().toFlowLabel() }
}
/**
@@ -37,9 +84,12 @@ module UnvalidatedDynamicMethodCall {
*/
abstract class Sanitizer extends DataFlow::Node {
/**
* Gets the flow label blocked by this sanitizer.
* Gets a flow state blocked by this sanitizer.
*/
DataFlow::FlowLabel getFlowLabel() { result.isTaint() }
FlowState getAFlowState() { result = FlowState::taint() }
/** DEPRECATED. Use `getAFlowState()` instead. */
deprecated DataFlow::FlowLabel getFlowLabel() { result = this.getAFlowState().toFlowLabel() }
/**
* DEPRECATED. Use sanitizer nodes instead.
@@ -64,16 +114,16 @@ module UnvalidatedDynamicMethodCall {
predicate blocksExpr(boolean outcome, Expr e) { none() }
/**
* Holds if this node acts as a barrier for `label`, blocking further flow from `e` if `this` evaluates to `outcome`.
* Holds if this node acts as a barrier for `state`, blocking further flow from `e` if `this` evaluates to `outcome`.
*/
predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) { none() }
predicate blocksExpr(boolean outcome, Expr e, FlowState state) { none() }
/** DEPRECATED. Use `blocksExpr` instead. */
deprecated predicate sanitizes(boolean outcome, Expr e) { this.blocksExpr(outcome, e) }
/** DEPRECATED. Use `blocksExpr` instead. */
deprecated predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
this.blocksExpr(outcome, e, label)
this.blocksExpr(outcome, e, FlowState::fromFlowLabel(label))
}
}
@@ -93,7 +143,7 @@ module UnvalidatedDynamicMethodCall {
* A flow label describing values read from a user-controlled property that
* may not be functions.
*/
abstract class MaybeNonFunction extends DataFlow::FlowLabel {
abstract deprecated class MaybeNonFunction extends DataFlow::FlowLabel {
MaybeNonFunction() { this = "MaybeNonFunction" }
}
@@ -101,7 +151,7 @@ module UnvalidatedDynamicMethodCall {
* A flow label describing values read from a user-controlled property that
* may originate from a prototype object.
*/
abstract class MaybeFromProto extends DataFlow::FlowLabel {
abstract deprecated class MaybeFromProto extends DataFlow::FlowLabel {
MaybeFromProto() { this = "MaybeFromProto" }
}
@@ -134,14 +184,14 @@ module UnvalidatedDynamicMethodCall {
)
}
override DataFlow::FlowLabel getFlowLabel() {
result instanceof MaybeNonFunction and
override FlowState getAFlowState() {
result = FlowState::maybeNonFunction() and
// don't flag if the type inference can prove that it is a function;
// this complements the `FunctionCheck` sanitizer below: the type inference can
// detect more checks locally, but doesn't provide inter-procedural reasoning
this.analyze().getAType() != TTFunction()
or
result instanceof MaybeFromProto
result = FlowState::maybeFromProto()
}
}
@@ -155,10 +205,10 @@ module UnvalidatedDynamicMethodCall {
FunctionCheck() { TaintTracking::isTypeofGuard(astNode, operand, "function") }
override predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
outcome = astNode.getPolarity() and
e = operand and
label instanceof MaybeNonFunction
state = FlowState::maybeNonFunction()
}
}

View File

@@ -13,14 +13,14 @@ import semmle.javascript.frameworks.Express
import PropertyInjectionShared
private import semmle.javascript.dataflow.InferredTypes
import UnvalidatedDynamicMethodCallCustomizations::UnvalidatedDynamicMethodCall
private import DataFlow::FlowLabel
private import UnvalidatedDynamicMethodCallCustomizations::UnvalidatedDynamicMethodCall as UnvalidatedDynamicMethodCall
// Materialize flow labels
private class ConcreteMaybeNonFunction extends MaybeNonFunction {
deprecated private class ConcreteMaybeNonFunction extends MaybeNonFunction {
ConcreteMaybeNonFunction() { this = this }
}
private class ConcreteMaybeFromProto extends MaybeFromProto {
deprecated private class ConcreteMaybeFromProto extends MaybeFromProto {
ConcreteMaybeFromProto() { this = this }
}
@@ -28,23 +28,21 @@ private class ConcreteMaybeFromProto extends MaybeFromProto {
* A taint-tracking configuration for reasoning about unvalidated dynamic method calls.
*/
module UnvalidatedDynamicMethodCallConfig implements DataFlow::StateConfigSig {
class FlowState = DataFlow::FlowLabel;
class FlowState = UnvalidatedDynamicMethodCall::FlowState;
predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
source.(Source).getFlowLabel() = label
predicate isSource(DataFlow::Node source, FlowState label) {
source.(Source).getAFlowState() = label
}
predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
sink.(Sink).getFlowLabel() = label
}
predicate isSink(DataFlow::Node sink, FlowState label) { sink.(Sink).getAFlowState() = label }
predicate isBarrier(DataFlow::Node node, DataFlow::FlowLabel label) {
node.(Sanitizer).getFlowLabel() = label
predicate isBarrier(DataFlow::Node node, FlowState label) {
node.(Sanitizer).getAFlowState() = label
or
TaintTracking::defaultSanitizer(node) and
label.isTaint()
label = FlowState::taint()
or
node = DataFlow::MakeLabeledBarrierGuard<BarrierGuard>::getABarrierNode(label)
node = DataFlow::MakeStateBarrierGuard<FlowState, BarrierGuard>::getABarrierNode(label)
}
predicate isBarrier(DataFlow::Node node) {
@@ -52,19 +50,18 @@ module UnvalidatedDynamicMethodCallConfig implements DataFlow::StateConfigSig {
}
predicate isAdditionalFlowStep(
DataFlow::Node src, DataFlow::FlowLabel srclabel, DataFlow::Node dst,
DataFlow::FlowLabel dstlabel
DataFlow::Node src, FlowState srclabel, DataFlow::Node dst, FlowState dstlabel
) {
exists(DataFlow::PropRead read |
src = read.getPropertyNameExpr().flow() and
dst = read and
srclabel.isTaint() and
srclabel = FlowState::taint() and
(
dstlabel instanceof MaybeNonFunction
dstlabel = FlowState::maybeNonFunction()
or
// a property of `Object.create(null)` cannot come from a prototype
not PropertyInjection::isPrototypeLessObject(read.getBase().getALocalSource()) and
dstlabel instanceof MaybeFromProto
dstlabel = FlowState::maybeFromProto()
) and
// avoid overlapping results with unsafe dynamic method access query
not PropertyInjection::hasUnsafeMethods(read.getBase().getALocalSource())
@@ -74,10 +71,10 @@ module UnvalidatedDynamicMethodCallConfig implements DataFlow::StateConfigSig {
src = get.getArgument(0) and
dst = get
) and
srclabel.isTaint() and
dstlabel instanceof MaybeNonFunction
srclabel = FlowState::taint() and
dstlabel = FlowState::maybeNonFunction()
or
srclabel.isTaint() and
srclabel = FlowState::taint() and
TaintTracking::defaultTaintStep(src, dst) and
srclabel = dstlabel
}
@@ -118,6 +115,7 @@ deprecated class Configuration extends TaintTracking::Configuration {
DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel,
DataFlow::FlowLabel dstlabel
) {
UnvalidatedDynamicMethodCallConfig::isAdditionalFlowStep(src, srclabel, dst, dstlabel)
UnvalidatedDynamicMethodCallConfig::isAdditionalFlowStep(src,
FlowState::fromFlowLabel(srclabel), dst, FlowState::fromFlowLabel(dstlabel))
}
}