JS: Migrate ExceptionXss

This commit is contained in:
Asger F
2024-12-12 14:13:27 +01:00
parent d9a43dbd85
commit 42a7208704
2 changed files with 52 additions and 15 deletions

View File

@@ -14,15 +14,48 @@ import javascript
module ExceptionXss {
private import Xss::Shared as Shared
private newtype TFlowState =
TThrown() or
TNotYetThrown()
/** A flow state to associate with a tracked value. */
class FlowState extends TFlowState {
/** Gets a string representation fo this flow state */
string toString() {
this = TThrown() and result = "thrown"
or
this = TNotYetThrown() and result = "not-yet-thrown"
}
deprecated DataFlow::FlowLabel toFlowLabel() {
this = TThrown() and result.isTaint()
or
this = TNotYetThrown() and result instanceof NotYetThrown
}
}
/** Predicates for working with flow states. */
module FlowState {
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
/** A tainted value originating from a thrown and caught exception. */
FlowState thrown() { result = TThrown() }
/** A value that has not yet been thrown. */
FlowState notYetThrown() { result = TNotYetThrown() }
}
/** A data flow source for XSS caused by interpreting exception or error text as HTML. */
abstract class Source extends DataFlow::Node {
/**
* Gets a flow label to associate with this source.
* Gets a flow state to associate with this source.
*
* For sources that should pass through a `throw/catch` before reaching the sink, use the
* `NotYetThrown` labe. Otherwise use `taint` (the default).
* `FlowState::notYetThrown()` state. Otherwise use `FlowState::thrown()` (the default).
*/
DataFlow::FlowLabel getAFlowLabel() { result.isTaint() }
FlowState getAFlowState() { result = FlowState::thrown() }
deprecated DataFlow::FlowLabel getAFlowLabel() { result = this.getAFlowState().toFlowLabel() }
/**
* Gets a human-readable description of what type of error this refers to.
@@ -33,17 +66,19 @@ module ExceptionXss {
}
/**
* DEPRECATED. Use `FlowState` instead.
*
* A FlowLabel representing tainted data that has not been thrown in an exception.
* In the js/xss-through-exception query data-flow can only reach a sink after
* the data has been thrown as an exception, and data that has not been thrown
* as an exception therefore has this flow label, and only this flow label, associated with it.
*/
abstract class NotYetThrown extends DataFlow::FlowLabel {
abstract deprecated class NotYetThrown extends DataFlow::FlowLabel {
NotYetThrown() { this = "NotYetThrown" }
}
private class XssSourceAsSource extends Source instanceof Shared::Source {
override DataFlow::FlowLabel getAFlowLabel() { result instanceof NotYetThrown }
override FlowState getAFlowState() { result instanceof TNotYetThrown }
override string getDescription() { result = "Exception text" }
}

View File

@@ -8,6 +8,7 @@ import javascript
import DomBasedXssCustomizations::DomBasedXss as DomBasedXssCustom
import ReflectedXssCustomizations::ReflectedXss as ReflectedXssCustom
import ExceptionXssCustomizations::ExceptionXss
private import ExceptionXssCustomizations::ExceptionXss as ExceptionXss
private import semmle.javascript.dataflow.InferredTypes
import Xss::Shared as XssShared
@@ -71,7 +72,7 @@ predicate canThrowSensitiveInformation(DataFlow::Node node) {
}
// Materialize flow labels
private class ConcreteNotYetThrown extends NotYetThrown {
deprecated private class ConcreteNotYetThrown extends NotYetThrown {
ConcreteNotYetThrown() { this = this }
}
@@ -130,14 +131,14 @@ private DataFlow::Node getExceptionTarget(DataFlow::Node pred) {
* an exception.
*/
module ExceptionXssConfig implements DataFlow::StateConfigSig {
class FlowState = DataFlow::FlowLabel;
class FlowState = ExceptionXss::FlowState;
predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
source.(Source).getAFlowLabel() = label
predicate isSource(DataFlow::Node source, FlowState state) {
source.(Source).getAFlowState() = state
}
predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
sink instanceof XssShared::Sink and not label instanceof NotYetThrown
predicate isSink(DataFlow::Node sink, FlowState state) {
sink instanceof XssShared::Sink and not state = FlowState::notYetThrown()
}
predicate isBarrier(DataFlow::Node node) {
@@ -145,10 +146,10 @@ module ExceptionXssConfig implements DataFlow::StateConfigSig {
}
predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::FlowLabel inlbl, DataFlow::Node succ, DataFlow::FlowLabel outlbl
DataFlow::Node pred, FlowState inlbl, DataFlow::Node succ, FlowState outlbl
) {
inlbl instanceof NotYetThrown and
(outlbl.isTaint() or outlbl instanceof NotYetThrown) and
inlbl = FlowState::notYetThrown() and
outlbl = [FlowState::thrown(), FlowState::notYetThrown()] and
canThrowSensitiveInformation(pred) and
succ = getExceptionTarget(pred)
}
@@ -178,7 +179,8 @@ deprecated class Configuration extends TaintTracking::Configuration {
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
) {
ExceptionXssConfig::isAdditionalFlowStep(pred, inlbl, succ, outlbl)
ExceptionXssConfig::isAdditionalFlowStep(pred, FlowState::fromFlowLabel(inlbl), succ,
FlowState::fromFlowLabel(outlbl))
or
// All the usual taint-flow steps apply on data-flow before it has been thrown in an exception.
// Note: this step is not needed in StateConfigSig module since flow states inherit taint steps.