Skip to content

Commit ac14b6d

Browse files
committed
Create EndpointCharacteristics to replace all existing NotASinkReasons and LikelyNotASinkReasons
1 parent fadbdc1 commit ac14b6d

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ abstract class EndpointCharacteristic extends string {
4545
);
4646
}
4747

48+
/*
49+
* Characteristics that are indicative of a sink.
50+
* NOTE: Initially each sink type has only one characteristic, which is that it's a sink of this type in the standard
51+
* JavaScript libraries.
52+
*/
53+
4854
/**
4955
* Endpoints identified as "DomBasedXssSink" by the standard JavaScript libraries are XSS sinks with maximal confidence.
5056
*/
@@ -111,3 +117,317 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic {
111117
confidence = 1.0
112118
}
113119
}
120+
121+
/*
122+
* Characteristics that are indicative of not being a sink of any type.
123+
*/
124+
125+
/**
126+
* A characteristic that is an indicator of not being a sink of any type, because it's an argument that has a manual
127+
* model.
128+
*/
129+
abstract private class OtherModeledArgumentCharacteristic extends EndpointCharacteristic {
130+
bindingset[this]
131+
OtherModeledArgumentCharacteristic() { any() }
132+
}
133+
134+
/**
135+
* A characteristic that is an indicator of not being a sink of any type, because it's an argument to a function of a
136+
* builtin object.
137+
*/
138+
abstract private class ArgumentToBuiltinFunctionCharacteristic extends OtherModeledArgumentCharacteristic {
139+
bindingset[this]
140+
ArgumentToBuiltinFunctionCharacteristic() { any() }
141+
}
142+
143+
/**
144+
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type.
145+
*/
146+
abstract private class NotASinkCharacteristic extends OtherModeledArgumentCharacteristic {
147+
bindingset[this]
148+
NotASinkCharacteristic() { any() }
149+
150+
override predicate getImplications(
151+
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
152+
) {
153+
endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.9
154+
}
155+
}
156+
157+
/**
158+
* A medium-confidence characteristic that indicates that an endpoint is not a sink of any type.
159+
*
160+
* TODO: This class is currently not private, because the current extraction logic explicitly avoids including these
161+
* endpoints in the training data. We might want to change this in the future.
162+
*/
163+
abstract class LikelyNotASinkCharacteristic extends OtherModeledArgumentCharacteristic {
164+
bindingset[this]
165+
LikelyNotASinkCharacteristic() { any() }
166+
167+
override predicate getImplications(
168+
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
169+
) {
170+
endpointClass instanceof NegativeType and isPositiveIndicator = true and confidence = 0.6
171+
}
172+
}
173+
174+
private class LodashUnderscore extends NotASinkCharacteristic {
175+
LodashUnderscore() { this = "LodashUnderscoreArgument" }
176+
177+
override predicate getEndpoints(DataFlow::Node n) {
178+
any(LodashUnderscore::Member m).getACall().getAnArgument() = n
179+
}
180+
}
181+
182+
private class JQueryArgumentCharacteristic extends NotASinkCharacteristic {
183+
JQueryArgumentCharacteristic() { this = "JQueryArgument" }
184+
185+
override predicate getEndpoints(DataFlow::Node n) {
186+
any(JQuery::MethodCall m).getAnArgument() = n
187+
}
188+
}
189+
190+
private class ClientRequestCharacteristic extends NotASinkCharacteristic {
191+
ClientRequestCharacteristic() { this = "ClientRequest" }
192+
193+
override predicate getEndpoints(DataFlow::Node n) {
194+
exists(ClientRequest r |
195+
r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode()
196+
)
197+
}
198+
}
199+
200+
private class PromiseDefinitionCharacteristic extends NotASinkCharacteristic {
201+
PromiseDefinitionCharacteristic() { this = "PromiseDefinition" }
202+
203+
override predicate getEndpoints(DataFlow::Node n) {
204+
exists(PromiseDefinition p |
205+
n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument()
206+
)
207+
}
208+
}
209+
210+
private class CryptographicKeyCharacteristic extends NotASinkCharacteristic {
211+
CryptographicKeyCharacteristic() { this = "CryptographicKey" }
212+
213+
override predicate getEndpoints(DataFlow::Node n) { n instanceof CryptographicKey }
214+
}
215+
216+
private class CryptographicOperationFlowCharacteristic extends NotASinkCharacteristic {
217+
CryptographicOperationFlowCharacteristic() { this = "CryptographicOperationFlow" }
218+
219+
override predicate getEndpoints(DataFlow::Node n) {
220+
any(CryptographicOperation op).getInput() = n
221+
}
222+
}
223+
224+
private class LoggerMethodCharacteristic extends NotASinkCharacteristic {
225+
LoggerMethodCharacteristic() { this = "LoggerMethod" }
226+
227+
override predicate getEndpoints(DataFlow::Node n) {
228+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
229+
call.getCalleeName() = getAStandardLoggerMethodName()
230+
)
231+
}
232+
}
233+
234+
private class TimeoutCharacteristic extends NotASinkCharacteristic {
235+
TimeoutCharacteristic() { this = "Timeout" }
236+
237+
override predicate getEndpoints(DataFlow::Node n) {
238+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
239+
call.getCalleeName() = ["setTimeout", "clearTimeout"]
240+
)
241+
}
242+
}
243+
244+
private class ReceiverStorageCharacteristic extends NotASinkCharacteristic {
245+
ReceiverStorageCharacteristic() { this = "ReceiverStorage" }
246+
247+
override predicate getEndpoints(DataFlow::Node n) {
248+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
249+
call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"])
250+
)
251+
}
252+
}
253+
254+
private class StringStartsWithCharacteristic extends NotASinkCharacteristic {
255+
StringStartsWithCharacteristic() { this = "StringStartsWith" }
256+
257+
override predicate getEndpoints(DataFlow::Node n) {
258+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
259+
call instanceof StringOps::StartsWith
260+
)
261+
}
262+
}
263+
264+
private class StringEndsWithCharacteristic extends NotASinkCharacteristic {
265+
StringEndsWithCharacteristic() { this = "StringEndsWith" }
266+
267+
override predicate getEndpoints(DataFlow::Node n) {
268+
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof StringOps::EndsWith)
269+
}
270+
}
271+
272+
private class StringRegExpTestCharacteristic extends NotASinkCharacteristic {
273+
StringRegExpTestCharacteristic() { this = "StringRegExpTest" }
274+
275+
override predicate getEndpoints(DataFlow::Node n) {
276+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
277+
call instanceof StringOps::RegExpTest
278+
)
279+
}
280+
}
281+
282+
private class EventRegistrationCharacteristic extends NotASinkCharacteristic {
283+
EventRegistrationCharacteristic() { this = "EventRegistration" }
284+
285+
override predicate getEndpoints(DataFlow::Node n) {
286+
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventRegistration)
287+
}
288+
}
289+
290+
private class EventDispatchCharacteristic extends NotASinkCharacteristic {
291+
EventDispatchCharacteristic() { this = "EventDispatch" }
292+
293+
override predicate getEndpoints(DataFlow::Node n) {
294+
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventDispatch)
295+
}
296+
}
297+
298+
private class MembershipCandidateTestCharacteristic extends NotASinkCharacteristic {
299+
MembershipCandidateTestCharacteristic() { this = "MembershipCandidateTest" }
300+
301+
override predicate getEndpoints(DataFlow::Node n) {
302+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
303+
call = any(MembershipCandidate c).getTest()
304+
)
305+
}
306+
}
307+
308+
private class FileSystemAccessCharacteristic extends NotASinkCharacteristic {
309+
FileSystemAccessCharacteristic() { this = "FileSystemAccess" }
310+
311+
override predicate getEndpoints(DataFlow::Node n) {
312+
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof FileSystemAccess)
313+
}
314+
}
315+
316+
private class DatabaseAccessCharacteristic extends NotASinkCharacteristic {
317+
DatabaseAccessCharacteristic() { this = "DatabaseAccess" }
318+
319+
override predicate getEndpoints(DataFlow::Node n) {
320+
// TODO database accesses are less well defined than database query sinks, so this may cover unmodeled sinks on
321+
// existing database models
322+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
323+
[
324+
call, call.getAMethodCall()
325+
/* command pattern where the query is built, and then exec'ed later */ ] instanceof
326+
DatabaseAccess
327+
)
328+
}
329+
}
330+
331+
private class DomCharacteristic extends NotASinkCharacteristic {
332+
DomCharacteristic() { this = "DOM" }
333+
334+
override predicate getEndpoints(DataFlow::Node n) {
335+
exists(DataFlow::CallNode call | n = call.getAnArgument() | call = DOM::domValueRef())
336+
}
337+
}
338+
339+
private class NextFunctionCallCharacteristic extends NotASinkCharacteristic {
340+
NextFunctionCallCharacteristic() { this = "NextFunctionCall" }
341+
342+
override predicate getEndpoints(DataFlow::Node n) {
343+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
344+
call.getCalleeName() = "next" and
345+
exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall())
346+
)
347+
}
348+
}
349+
350+
private class DojoRequireCharacteristic extends NotASinkCharacteristic {
351+
DojoRequireCharacteristic() { this = "DojoRequire" }
352+
353+
override predicate getEndpoints(DataFlow::Node n) {
354+
exists(DataFlow::CallNode call | n = call.getAnArgument() |
355+
call = DataFlow::globalVarRef("dojo").getAPropertyRead("require").getACall()
356+
)
357+
}
358+
}
359+
360+
private class Base64ManipulationCharacteristic extends NotASinkCharacteristic {
361+
Base64ManipulationCharacteristic() { this = "Base64Manipulation" }
362+
363+
override predicate getEndpoints(DataFlow::Node n) {
364+
exists(Base64::Decode d | n = d.getInput()) or
365+
exists(Base64::Encode d | n = d.getInput())
366+
}
367+
}
368+
369+
private class ArgumentToArrayCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
370+
LikelyNotASinkCharacteristic {
371+
ArgumentToArrayCharacteristic() { this = "ArgumentToArray" }
372+
373+
override predicate getEndpoints(DataFlow::Node n) {
374+
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
375+
builtin instanceof DataFlow::ArrayCreationNode
376+
|
377+
receiver = [builtin.getAnInvocation(), builtin] and
378+
invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and
379+
invk.getAnArgument() = n
380+
)
381+
}
382+
}
383+
384+
private class ArgumentToBuiltinGlobalVarRefCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
385+
LikelyNotASinkCharacteristic {
386+
ArgumentToBuiltinGlobalVarRefCharacteristic() { this = "ArgumentToBuiltinGlobalVarRef" }
387+
388+
override predicate getEndpoints(DataFlow::Node n) {
389+
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
390+
builtin =
391+
DataFlow::globalVarRef([
392+
"Map", "Set", "WeakMap", "WeakSet", "Number", "Object", "String", "Array", "Error",
393+
"Math", "Boolean"
394+
])
395+
|
396+
receiver = [builtin.getAnInvocation(), builtin] and
397+
invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and
398+
invk.getAnArgument() = n
399+
)
400+
}
401+
}
402+
403+
private class ConstantReceiverCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
404+
NotASinkCharacteristic {
405+
ConstantReceiverCharacteristic() { this = "ConstantReceiver" }
406+
407+
override predicate getEndpoints(DataFlow::Node n) {
408+
exists(Expr primitive, MethodCallExpr c |
409+
primitive instanceof ConstantString or
410+
primitive instanceof NumberLiteral or
411+
primitive instanceof BooleanLiteral
412+
|
413+
c.calls(primitive, _) and
414+
c.getAnArgument() = n.asExpr()
415+
)
416+
}
417+
}
418+
419+
private class BuiltinCallNameCharacteristic extends ArgumentToBuiltinFunctionCharacteristic,
420+
NotASinkCharacteristic {
421+
BuiltinCallNameCharacteristic() { this = "BuiltinCallName" }
422+
423+
override predicate getEndpoints(DataFlow::Node n) {
424+
exists(DataFlow::CallNode call |
425+
call.getAnArgument() = n and
426+
call.getCalleeName() =
427+
[
428+
"indexOf", "hasOwnProperty", "substring", "isDecimal", "decode", "encode", "keys",
429+
"shift", "values", "forEach", "toString", "slice", "splice", "push", "isArray", "sort"
430+
]
431+
)
432+
}
433+
}

0 commit comments

Comments
 (0)