Create EndpointCharacteristics to replace all existing NotASinkReasons and LikelyNotASinkReasons

This commit is contained in:
tiferet
2022-11-08 13:37:49 -08:00
parent fadbdc1f63
commit ac14b6d685

View File

@@ -45,6 +45,12 @@ abstract class EndpointCharacteristic extends string {
);
}
/*
* Characteristics that are indicative of a sink.
* NOTE: Initially each sink type has only one characteristic, which is that it's a sink of this type in the standard
* JavaScript libraries.
*/
/**
* Endpoints identified as "DomBasedXssSink" by the standard JavaScript libraries are XSS sinks with maximal confidence.
*/
@@ -111,3 +117,317 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic {
confidence = 1.0
}
}
/*
* Characteristics that are indicative of not being a sink of any type.
*/
/**
* A characteristic that is an indicator of not being a sink of any type, because it's an argument that has a manual
* model.
*/
abstract private class OtherModeledArgumentCharacteristic extends EndpointCharacteristic {
bindingset[this]
OtherModeledArgumentCharacteristic() { any() }
}
/**
* A characteristic that is an indicator of not being a sink of any type, because it's an argument to a function of a
* builtin object.
*/
abstract private class ArgumentToBuiltinFunctionCharacteristic extends OtherModeledArgumentCharacteristic {
bindingset[this]
ArgumentToBuiltinFunctionCharacteristic() { any() }
}
/**
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type.
*/
abstract private class NotASinkCharacteristic extends OtherModeledArgumentCharacteristic {
bindingset[this]
NotASinkCharacteristic() { any() }
override predicate getImplications(
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
) {
endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.9
}
}
/**
* A medium-confidence characteristic that indicates that an endpoint is not a sink of any type.
*
* TODO: This class is currently not private, because the current extraction logic explicitly avoids including these
* endpoints in the training data. We might want to change this in the future.
*/
abstract class LikelyNotASinkCharacteristic extends OtherModeledArgumentCharacteristic {
bindingset[this]
LikelyNotASinkCharacteristic() { any() }
override predicate getImplications(
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
) {
endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.6
}
}
private class LodashUnderscore extends NotASinkCharacteristic {
LodashUnderscore() { this = "LodashUnderscoreArgument" }
override predicate getEndpoints(DataFlow::Node n) {
any(LodashUnderscore::Member m).getACall().getAnArgument() = n
}
}
private class JQueryArgumentCharacteristic extends NotASinkCharacteristic {
JQueryArgumentCharacteristic() { this = "JQueryArgument" }
override predicate getEndpoints(DataFlow::Node n) {
any(JQuery::MethodCall m).getAnArgument() = n
}
}
private class ClientRequestCharacteristic extends NotASinkCharacteristic {
ClientRequestCharacteristic() { this = "ClientRequest" }
override predicate getEndpoints(DataFlow::Node n) {
exists(ClientRequest r |
r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode()
)
}
}
private class PromiseDefinitionCharacteristic extends NotASinkCharacteristic {
PromiseDefinitionCharacteristic() { this = "PromiseDefinition" }
override predicate getEndpoints(DataFlow::Node n) {
exists(PromiseDefinition p |
n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument()
)
}
}
private class CryptographicKeyCharacteristic extends NotASinkCharacteristic {
CryptographicKeyCharacteristic() { this = "CryptographicKey" }
override predicate getEndpoints(DataFlow::Node n) { n instanceof CryptographicKey }
}
private class CryptographicOperationFlowCharacteristic extends NotASinkCharacteristic {
CryptographicOperationFlowCharacteristic() { this = "CryptographicOperationFlow" }
override predicate getEndpoints(DataFlow::Node n) {
any(CryptographicOperation op).getInput() = n
}
}
private class LoggerMethodCharacteristic extends NotASinkCharacteristic {
LoggerMethodCharacteristic() { this = "LoggerMethod" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call.getCalleeName() = getAStandardLoggerMethodName()
)
}
}
private class TimeoutCharacteristic extends NotASinkCharacteristic {
TimeoutCharacteristic() { this = "Timeout" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call.getCalleeName() = ["setTimeout", "clearTimeout"]
)
}
}
private class ReceiverStorageCharacteristic extends NotASinkCharacteristic {
ReceiverStorageCharacteristic() { this = "ReceiverStorage" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"])
)
}
}
private class StringStartsWithCharacteristic extends NotASinkCharacteristic {
StringStartsWithCharacteristic() { this = "StringStartsWith" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call instanceof StringOps::StartsWith
)
}
}
private class StringEndsWithCharacteristic extends NotASinkCharacteristic {
StringEndsWithCharacteristic() { this = "StringEndsWith" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof StringOps::EndsWith)
}
}
private class StringRegExpTestCharacteristic extends NotASinkCharacteristic {
StringRegExpTestCharacteristic() { this = "StringRegExpTest" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call instanceof StringOps::RegExpTest
)
}
}
private class EventRegistrationCharacteristic extends NotASinkCharacteristic {
EventRegistrationCharacteristic() { this = "EventRegistration" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventRegistration)
}
}
private class EventDispatchCharacteristic extends NotASinkCharacteristic {
EventDispatchCharacteristic() { this = "EventDispatch" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventDispatch)
}
}
private class MembershipCandidateTestCharacteristic extends NotASinkCharacteristic {
MembershipCandidateTestCharacteristic() { this = "MembershipCandidateTest" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call = any(MembershipCandidate c).getTest()
)
}
}
private class FileSystemAccessCharacteristic extends NotASinkCharacteristic {
FileSystemAccessCharacteristic() { this = "FileSystemAccess" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof FileSystemAccess)
}
}
private class DatabaseAccessCharacteristic extends NotASinkCharacteristic {
DatabaseAccessCharacteristic() { this = "DatabaseAccess" }
override predicate getEndpoints(DataFlow::Node n) {
// TODO database accesses are less well defined than database query sinks, so this may cover unmodeled sinks on
// existing database models
exists(DataFlow::CallNode call | n = call.getAnArgument() |
[
call, call.getAMethodCall()
/* command pattern where the query is built, and then exec'ed later */ ] instanceof
DatabaseAccess
)
}
}
private class DomCharacteristic extends NotASinkCharacteristic {
DomCharacteristic() { this = "DOM" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() | call = DOM::domValueRef())
}
}
private class NextFunctionCallCharacteristic extends NotASinkCharacteristic {
NextFunctionCallCharacteristic() { this = "NextFunctionCall" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call.getCalleeName() = "next" and
exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall())
)
}
}
private class DojoRequireCharacteristic extends NotASinkCharacteristic {
DojoRequireCharacteristic() { this = "DojoRequire" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call | n = call.getAnArgument() |
call = DataFlow::globalVarRef("dojo").getAPropertyRead("require").getACall()
)
}
}
private class Base64ManipulationCharacteristic extends NotASinkCharacteristic {
Base64ManipulationCharacteristic() { this = "Base64Manipulation" }
override predicate getEndpoints(DataFlow::Node n) {
exists(Base64::Decode d | n = d.getInput()) or
exists(Base64::Encode d | n = d.getInput())
}
}
private class ArgumentToArrayCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
LikelyNotASinkCharacteristic {
ArgumentToArrayCharacteristic() { this = "ArgumentToArray" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
builtin instanceof DataFlow::ArrayCreationNode
|
receiver = [builtin.getAnInvocation(), builtin] and
invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and
invk.getAnArgument() = n
)
}
}
private class ArgumentToBuiltinGlobalVarRefCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
LikelyNotASinkCharacteristic {
ArgumentToBuiltinGlobalVarRefCharacteristic() { this = "ArgumentToBuiltinGlobalVarRef" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
builtin =
DataFlow::globalVarRef([
"Map", "Set", "WeakMap", "WeakSet", "Number", "Object", "String", "Array", "Error",
"Math", "Boolean"
])
|
receiver = [builtin.getAnInvocation(), builtin] and
invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and
invk.getAnArgument() = n
)
}
}
private class ConstantReceiverCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
NotASinkCharacteristic {
ConstantReceiverCharacteristic() { this = "ConstantReceiver" }
override predicate getEndpoints(DataFlow::Node n) {
exists(Expr primitive, MethodCallExpr c |
primitive instanceof ConstantString or
primitive instanceof NumberLiteral or
primitive instanceof BooleanLiteral
|
c.calls(primitive, _) and
c.getAnArgument() = n.asExpr()
)
}
}
private class BuiltinCallNameCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
NotASinkCharacteristic {
BuiltinCallNameCharacteristic() { this = "BuiltinCallName" }
override predicate getEndpoints(DataFlow::Node n) {
exists(DataFlow::CallNode call |
call.getAnArgument() = n and
call.getCalleeName() =
[
"indexOf", "hasOwnProperty", "substring", "isDecimal", "decode", "encode", "keys",
"shift", "values", "forEach", "toString", "slice", "splice", "push", "isArray", "sort"
]
)
}
}