JS: Track types of classes in data flow

This commit is contained in:
Asger F
2025-02-14 12:39:57 +01:00
parent d3c4b5d493
commit ff7bc7c25e
2 changed files with 34 additions and 6 deletions

View File

@@ -558,15 +558,19 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
newtype TDataFlowType =
TFunctionType(Function f) or
TInstanceType(DataFlow::ClassNode cls) or
TAnyType()
class DataFlowType extends TDataFlowType {
string toDebugString() {
this instanceof TFunctionType and
result =
"TFunctionType(" + this.asFunction().toString() + ") at line " +
this.asFunction().getLocation().getStartLine()
or
result =
"TInstanceType(" + this.asInstanceOfClass().toString() + ") at line " +
this.asInstanceOfClass().getLocation().getStartLine()
or
this instanceof TAnyType and result = "TAnyType"
}
@@ -575,13 +579,25 @@ class DataFlowType extends TDataFlowType {
}
Function asFunction() { this = TFunctionType(result) }
DataFlow::ClassNode asInstanceOfClass() { this = TInstanceType(result) }
}
private predicate typeStrongerThan1(DataFlowType t1, DataFlowType t2) {
// 't1' is a subclass of 't2'
t1.asInstanceOfClass() = t2.asInstanceOfClass().getADirectSubClass()
}
/**
* Holds if `t1` is strictly stronger than `t2`.
*/
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
t1 instanceof TFunctionType and t2 = TAnyType()
typeStrongerThan1(t1, t2)
or
// Ensure all types are transitively stronger than 'any'
not typeStrongerThan1(t1, _) and
not t1 = TAnyType() and
t2 = TAnyType()
}
private DataFlowType getPreciseType(Node node) {
@@ -590,6 +606,9 @@ private DataFlowType getPreciseType(Node node) {
result = TFunctionType(f)
)
or
result.asInstanceOfClass() =
unique(DataFlow::ClassNode cls | cls.getAnInstanceReference().getALocalUse() = node)
or
result = getPreciseType(node.getImmediatePredecessor())
or
result = getPreciseType(node.(PostUpdateNode).getPreUpdateNode())
@@ -683,18 +702,27 @@ predicate neverSkipInPathGraph(Node node) {
string ppReprType(DataFlowType t) { none() }
pragma[inline]
private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) {
private predicate compatibleTypesWithAny(DataFlowType t1, DataFlowType t2) {
t1 != TAnyType() and
t2 = TAnyType()
}
pragma[nomagic]
private predicate compatibleTypes1(DataFlowType t1, DataFlowType t2) {
t1.asInstanceOfClass().getADirectSubClass+() = t2.asInstanceOfClass()
}
pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 = t2
or
compatibleTypesNonSymRefl(t1, t2)
compatibleTypesWithAny(t1, t2)
or
compatibleTypesNonSymRefl(t2, t1)
compatibleTypesWithAny(t2, t1)
or
compatibleTypes1(t1, t2)
or
compatibleTypes1(t2, t1)
}
predicate forceHighPrecision(Content c) { none() }

View File

@@ -29,6 +29,6 @@ class Subclass3 extends Base {
this.baseMethod("safe");
}
subclassMethod(x) {
sink(x); // $ SPURIOUS: hasValueFlow=sub1 hasValueFlow=sub2
sink(x);
}
}