@@ -45,6 +45,12 @@ abstract class EndpointCharacteristic extends string {
45
45
) ;
46
46
}
47
47
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
+
48
54
/**
49
55
* Endpoints identified as "DomBasedXssSink" by the standard JavaScript libraries are XSS sinks with maximal confidence.
50
56
*/
@@ -111,3 +117,317 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic {
111
117
confidence = 1.0
112
118
}
113
119
}
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