JS: Migrate TaintedObject to a CommonFlowState

This commit is contained in:
Asger F
2024-12-11 15:14:47 +01:00
parent 14ca1c134b
commit 5f42a715f6
3 changed files with 57 additions and 29 deletions

View File

@@ -4,11 +4,13 @@
private import javascript
private import TaintedUrlSuffixCustomizations
private import TaintedObjectCustomizations
private newtype TFlowState =
TTaint() or
TTaintedUrlSuffix() or
TTaintedPrefix()
TTaintedPrefix() or
TTaintedObject()
/**
* A flow state indicating which part of a value is tainted.
@@ -30,6 +32,12 @@ class FlowState extends TFlowState {
*/
predicate isTaintedPrefix() { this = TTaintedPrefix() }
/**
* Holds if this represents a deeply tainted object, such as a JSON object
* parsed from user-controlled data.
*/
predicate isTaintedObject() { this = TTaintedObject() }
/** Gets a string representation of this flow state. */
string toString() {
this.isTaint() and result = "taint"
@@ -37,6 +45,8 @@ class FlowState extends TFlowState {
this.isTaintedUrlSuffix() and result = "tainted-url-suffix"
or
this.isTaintedPrefix() and result = "tainted-prefix"
or
this.isTaintedObject() and result = "tainted-object"
}
/** DEPRECATED. Gets the corresponding flow label. */
@@ -46,6 +56,8 @@ class FlowState extends TFlowState {
this.isTaintedUrlSuffix() and result = TaintedUrlSuffix::label()
or
this.isTaintedPrefix() and result = "PrefixString"
or
this.isTaintedObject() and result = TaintedObject::label()
}
}
@@ -67,6 +79,12 @@ module FlowState {
*/
FlowState taintedPrefix() { result.isTaintedPrefix() }
/**
* Gets the flow state representing a deeply tainted object, such as a JSON object
* parsed from user-controlled data.
*/
FlowState taintedObject() { result.isTaintedObject() }
/** DEPRECATED. Gets the flow state corresponding to `label`. */
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
}

View File

@@ -7,10 +7,10 @@
*
* To track deeply tainted objects, a flow-tracking configuration should generally include the following:
*
* 1. One or more sinks associated with the label `TaintedObject::label()`.
* 2. The sources from `TaintedObject::isSource`.
* 3. The flow steps from `TaintedObject::step`.
* 4. The sanitizing guards `TaintedObject::SanitizerGuard`.
* 1. One or more sinks associated with the flow state `FlowState::taintedObject()`.
* 2. The sources from `TaintedObject::Source`.
* 3. The flow steps from `TaintedObject::isAdditionalFlowStep`.
* 4. The barriers from `TaintedObject::SanitizerGuard::getABarrierNode(state)`.
*/
import javascript
@@ -22,35 +22,39 @@ module TaintedObject {
import TaintedObjectCustomizations::TaintedObject
// Materialize flow labels
private class ConcreteTaintedObjectLabel extends TaintedObjectLabel {
deprecated private class ConcreteTaintedObjectLabel extends TaintedObjectLabel {
ConcreteTaintedObjectLabel() { this = this }
}
deprecated predicate step(Node src, Node trg, FlowLabel inlbl, FlowLabel outlbl) {
isAdditionalFlowStep(src, FlowState::fromFlowLabel(inlbl), trg, FlowState::fromFlowLabel(outlbl))
}
/**
* Holds for the flows steps that are relevant for tracking user-controlled JSON objects.
*/
predicate step(Node src, Node trg, FlowLabel inlbl, FlowLabel outlbl) {
predicate isAdditionalFlowStep(Node src, FlowState inlbl, Node trg, FlowState outlbl) {
// JSON parsers map tainted inputs to tainted JSON
inlbl.isDataOrTaint() and
outlbl = label() and
inlbl.isTaint() and
outlbl.isTaintedObject() and
exists(JsonParserCall parse |
src = parse.getInput() and
trg = parse.getOutput()
)
or
// Property reads preserve deep object taint.
inlbl = label() and
outlbl = label() and
inlbl.isTaintedObject() and
outlbl.isTaintedObject() and
trg.(PropRead).getBase() = src
or
// Property projection preserves deep object taint
inlbl = label() and
outlbl = label() and
inlbl.isTaintedObject() and
outlbl.isTaintedObject() and
trg.(PropertyProjection).getObject() = src
or
// Extending objects preserves deep object taint
inlbl = label() and
outlbl = label() and
inlbl.isTaintedObject() and
outlbl.isTaintedObject() and
exists(ExtendCall call |
src = call.getAnOperand() and
trg = call
@@ -60,8 +64,8 @@ module TaintedObject {
)
or
// Spreading into an object preserves deep object taint: `p -> { ...p }`
inlbl = label() and
outlbl = label() and
inlbl.isTaintedObject() and
outlbl.isTaintedObject() and
exists(ObjectLiteralNode obj |
src = obj.getASpreadProperty() and
trg = obj
@@ -69,9 +73,13 @@ module TaintedObject {
}
/**
* DEPRECATED. Use the `Source` class and `FlowState#isTaintedObject()` directly.
*
* Holds if `node` is a source of JSON taint and label is the JSON taint label.
*/
predicate isSource(Node source, FlowLabel label) { source instanceof Source and label = label() }
deprecated predicate isSource(Node source, FlowLabel label) {
source instanceof Source and label = label()
}
/** Request input accesses as a JSON source. */
private class RequestInputAsSource extends Source {
@@ -86,11 +94,11 @@ module TaintedObject {
predicate blocksExpr(boolean outcome, Expr e) { none() }
/** Holds if this node blocks flow of `label` through `e`, provided it evaluates to `outcome`. */
predicate blocksExpr(boolean outcome, Expr e, FlowLabel label) { none() }
predicate blocksExpr(boolean outcome, Expr e, FlowState label) { none() }
/** DEPRECATED. Use `blocksExpr` instead. */
deprecated predicate sanitizes(boolean outcome, Expr e, FlowLabel label) {
this.blocksExpr(outcome, e, label)
this.blocksExpr(outcome, e, FlowState::fromFlowLabel(label))
}
/** DEPRECATED. Use `blocksExpr` instead. */
@@ -111,7 +119,7 @@ module TaintedObject {
/**
* A sanitizer guard that blocks deep object taint.
*/
module SanitizerGuard = DataFlow::MakeLabeledBarrierGuard<SanitizerGuard>;
module SanitizerGuard = DataFlow::MakeStateBarrierGuard<FlowState, SanitizerGuard>;
/**
* A test of form `typeof x === "something"`, preventing `x` from being an object in some cases.
@@ -133,10 +141,10 @@ module TaintedObject {
)
}
override predicate blocksExpr(boolean outcome, Expr e, FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
polarity = outcome and
e = operand and
label = label()
state.isTaintedObject()
}
}
@@ -161,8 +169,8 @@ module TaintedObject {
.getACall()
}
override predicate blocksExpr(boolean outcome, Expr e, FlowLabel lbl) {
e = super.getAnArgument().asExpr() and outcome = true and lbl = label()
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
e = super.getAnArgument().asExpr() and outcome = true and state.isTaintedObject()
}
}
@@ -175,10 +183,10 @@ module TaintedObject {
JsonSchemaValidationGuard() { this = call.getAValidationResultAccess(polarity) }
override predicate blocksExpr(boolean outcome, Expr e, FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
outcome = polarity and
e = call.getInput().asExpr() and
label = label()
state.isTaintedObject()
}
}
}

View File

@@ -7,8 +7,10 @@ import javascript
/** Provides classes and predicates for reasoning about deeply tainted objects. */
module TaintedObject {
import CommonFlowState
/** A flow label representing a deeply tainted object. */
abstract class TaintedObjectLabel extends DataFlow::FlowLabel {
abstract deprecated class TaintedObjectLabel extends DataFlow::FlowLabel {
TaintedObjectLabel() { this = "tainted-object" }
}
@@ -19,7 +21,7 @@ module TaintedObject {
*
* Note that the presence of the this label generally implies the presence of the `taint` label as well.
*/
DataFlow::FlowLabel label() { result instanceof TaintedObjectLabel }
deprecated DataFlow::FlowLabel label() { result instanceof TaintedObjectLabel }
/**
* A source of a user-controlled deep object.