mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
Create EndpointCharacteristics to replace all existing NotASinkReasons and LikelyNotASinkReasons
This commit is contained in:
@@ -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.
|
* 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
|
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"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user