@@ -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