5
5
*
6
6
* The CSV specification has the following columns:
7
7
* - Sources:
8
- * `namespace; type; subtypes; name; signature; ext; output; kind`
8
+ * `namespace; type; subtypes; name; signature; ext; output; kind; provenance `
9
9
* - Sinks:
10
- * `namespace; type; subtypes; name; signature; ext; input; kind`
10
+ * `namespace; type; subtypes; name; signature; ext; input; kind; provenance `
11
11
* - Summaries:
12
- * `namespace; type; subtypes; name; signature; ext; input; output; kind`
13
- *
12
+ * `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance`
13
+ * - Negative Summaries:
14
+ * `namespace; type; name; signature; provenance`
14
15
* The interpretation of a row is similar to API-graphs with a left-to-right
15
16
* reading.
16
17
* 1. The `namespace` column selects a namespace.
@@ -163,11 +164,27 @@ class SummaryModelCsv extends Unit {
163
164
abstract predicate row ( string row ) ;
164
165
}
165
166
166
- private predicate sourceModel ( string row ) { any ( SourceModelCsv s ) .row ( row ) }
167
+ /**
168
+ * A unit class for adding negative summary model rows.
169
+ *
170
+ * Extend this class to add additional flow summary definitions.
171
+ */
172
+ class NegativeSummaryModelCsv extends Unit {
173
+ /** Holds if `row` specifies a negative summary definition. */
174
+ abstract predicate row ( string row ) ;
175
+ }
176
+
177
+ /** Holds if `row` is a source model. */
178
+ predicate sourceModel ( string row ) { any ( SourceModelCsv s ) .row ( row ) }
167
179
168
- private predicate sinkModel ( string row ) { any ( SinkModelCsv s ) .row ( row ) }
180
+ /** Holds if `row` is a sink model. */
181
+ predicate sinkModel ( string row ) { any ( SinkModelCsv s ) .row ( row ) }
169
182
170
- private predicate summaryModel ( string row ) { any ( SummaryModelCsv s ) .row ( row ) }
183
+ /** Holds if `row` is a summary model. */
184
+ predicate summaryModel ( string row ) { any ( SummaryModelCsv s ) .row ( row ) }
185
+
186
+ /** Holds if `row` is a negative summary model. */
187
+ predicate negativeSummaryModel ( string row ) { any ( NegativeSummaryModelCsv s ) .row ( row ) }
171
188
172
189
/** Holds if a source model exists for the given parameters. */
173
190
predicate sourceModel (
@@ -230,6 +247,20 @@ predicate summaryModel(
230
247
)
231
248
}
232
249
250
+ /** Holds if a summary model exists indicating there is no flow for the given parameters. */
251
+ predicate negativeSummaryModel (
252
+ string namespace , string type , string name , string signature , string provenance
253
+ ) {
254
+ exists ( string row |
255
+ negativeSummaryModel ( row ) and
256
+ row .splitAt ( ";" , 0 ) = namespace and
257
+ row .splitAt ( ";" , 1 ) = type and
258
+ row .splitAt ( ";" , 2 ) = name and
259
+ row .splitAt ( ";" , 3 ) = signature and
260
+ row .splitAt ( ";" , 4 ) = provenance
261
+ )
262
+ }
263
+
233
264
private predicate relevantNamespace ( string namespace ) {
234
265
sourceModel ( namespace , _, _, _, _, _, _, _, _) or
235
266
sinkModel ( namespace , _, _, _, _, _, _, _, _) or
@@ -286,38 +317,7 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
286
317
287
318
/** Provides a query predicate to check the CSV data for validation errors. */
288
319
module CsvValidation {
289
- /** Holds if some row in a CSV-based flow model appears to contain typos. */
290
- query predicate invalidModelRow ( string msg ) {
291
- exists (
292
- string pred , string namespace , string type , string name , string signature , string ext ,
293
- string provenance
294
- |
295
- sourceModel ( namespace , type , _, name , signature , ext , _, _, provenance ) and pred = "source"
296
- or
297
- sinkModel ( namespace , type , _, name , signature , ext , _, _, provenance ) and pred = "sink"
298
- or
299
- summaryModel ( namespace , type , _, name , signature , ext , _, _, _, provenance ) and
300
- pred = "summary"
301
- |
302
- not namespace .regexpMatch ( "[a-zA-Z0-9_\\.]+" ) and
303
- msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
304
- or
305
- not type .regexpMatch ( "[a-zA-Z0-9_<>,\\+]+" ) and
306
- msg = "Dubious type \"" + type + "\" in " + pred + " model."
307
- or
308
- not name .regexpMatch ( "[a-zA-Z0-9_<>,]*" ) and
309
- msg = "Dubious member name \"" + name + "\" in " + pred + " model."
310
- or
311
- not signature .regexpMatch ( "|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)" ) and
312
- msg = "Dubious signature \"" + signature + "\" in " + pred + " model."
313
- or
314
- not ext .regexpMatch ( "|Attribute" ) and
315
- msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
316
- or
317
- not provenance = [ "manual" , "generated" ] and
318
- msg = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
319
- )
320
- or
320
+ private string getInvalidModelInput ( ) {
321
321
exists ( string pred , AccessPath input , string part |
322
322
sinkModel ( _, _, _, _, _, _, input , _, _) and pred = "sink"
323
323
or
@@ -332,9 +332,11 @@ module CsvValidation {
332
332
part = input .getToken ( _) and
333
333
parseParam ( part , _)
334
334
) and
335
- msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
335
+ result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
336
336
)
337
- or
337
+ }
338
+
339
+ private string getInvalidModelOutput ( ) {
338
340
exists ( string pred , string output , string part |
339
341
sourceModel ( _, _, _, _, _, _, output , _, _) and pred = "source"
340
342
or
@@ -343,58 +345,123 @@ module CsvValidation {
343
345
invalidSpecComponent ( output , part ) and
344
346
not part = "" and
345
347
not ( part = [ "Argument" , "Parameter" ] and pred = "source" ) and
346
- msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
348
+ result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
349
+ )
350
+ }
351
+
352
+ private string getInvalidModelKind ( ) {
353
+ exists ( string row , string kind | summaryModel ( row ) |
354
+ kind = row .splitAt ( ";" , 8 ) and
355
+ not kind = [ "taint" , "value" ] and
356
+ result = "Invalid kind \"" + kind + "\" in summary model."
347
357
)
348
358
or
359
+ exists ( string row , string kind | sinkModel ( row ) |
360
+ kind = row .splitAt ( ";" , 7 ) and
361
+ not kind = [ "code" , "sql" , "xss" , "remote" , "html" ] and
362
+ not kind .matches ( "encryption-%" ) and
363
+ result = "Invalid kind \"" + kind + "\" in sink model."
364
+ )
365
+ or
366
+ exists ( string row , string kind | sourceModel ( row ) |
367
+ kind = row .splitAt ( ";" , 7 ) and
368
+ not kind = [ "local" , "file" ] and
369
+ result = "Invalid kind \"" + kind + "\" in source model."
370
+ )
371
+ }
372
+
373
+ private string getInvalidModelSubtype ( ) {
374
+ exists ( string pred , string row |
375
+ sourceModel ( row ) and pred = "source"
376
+ or
377
+ sinkModel ( row ) and pred = "sink"
378
+ or
379
+ summaryModel ( row ) and pred = "summary"
380
+ |
381
+ exists ( string b |
382
+ b = row .splitAt ( ";" , 2 ) and
383
+ not b = [ "true" , "false" ] and
384
+ result = "Invalid boolean \"" + b + "\" in " + pred + " model."
385
+ )
386
+ )
387
+ }
388
+
389
+ private string getInvalidModelColumnCount ( ) {
349
390
exists ( string pred , string row , int expect |
350
391
sourceModel ( row ) and expect = 9 and pred = "source"
351
392
or
352
393
sinkModel ( row ) and expect = 9 and pred = "sink"
353
394
or
354
395
summaryModel ( row ) and expect = 10 and pred = "summary"
396
+ or
397
+ negativeSummaryModel ( row ) and expect = 5 and pred = "negative summary"
355
398
|
356
399
exists ( int cols |
357
400
cols = 1 + max ( int n | exists ( row .splitAt ( ";" , n ) ) ) and
358
401
cols != expect and
359
- msg =
402
+ result =
360
403
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
361
404
" in " + row + "."
362
405
)
363
- or
364
- exists ( string b |
365
- b = row .splitAt ( ";" , 2 ) and
366
- not b = [ "true" , "false" ] and
367
- msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
368
- )
369
406
)
370
- or
371
- exists ( string row , string kind | summaryModel ( row ) |
372
- kind = row .splitAt ( ";" , 8 ) and
373
- not kind = [ "taint" , "value" ] and
374
- msg = "Invalid kind \"" + kind + "\" in summary model."
375
- )
376
- or
377
- exists ( string row , string kind | sinkModel ( row ) |
378
- kind = row .splitAt ( ";" , 7 ) and
379
- not kind = [ "code" , "sql" , "xss" , "remote" , "html" ] and
380
- not kind .matches ( "encryption-%" ) and
381
- msg = "Invalid kind \"" + kind + "\" in sink model."
382
- )
383
- or
384
- exists ( string row , string kind | sourceModel ( row ) |
385
- kind = row .splitAt ( ";" , 7 ) and
386
- not kind = [ "local" , "file" ] and
387
- msg = "Invalid kind \"" + kind + "\" in source model."
407
+ }
408
+
409
+ private string getInvalidModelSignature ( ) {
410
+ exists (
411
+ string pred , string namespace , string type , string name , string signature , string ext ,
412
+ string provenance
413
+ |
414
+ sourceModel ( namespace , type , _, name , signature , ext , _, _, provenance ) and pred = "source"
415
+ or
416
+ sinkModel ( namespace , type , _, name , signature , ext , _, _, provenance ) and pred = "sink"
417
+ or
418
+ summaryModel ( namespace , type , _, name , signature , ext , _, _, _, provenance ) and
419
+ pred = "summary"
420
+ or
421
+ negativeSummaryModel ( namespace , type , name , signature , provenance ) and
422
+ ext = "" and
423
+ pred = "negative summary"
424
+ |
425
+ not namespace .regexpMatch ( "[a-zA-Z0-9_\\.]+" ) and
426
+ result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
427
+ or
428
+ not type .regexpMatch ( "[a-zA-Z0-9_<>,\\+]+" ) and
429
+ result = "Dubious type \"" + type + "\" in " + pred + " model."
430
+ or
431
+ not name .regexpMatch ( "[a-zA-Z0-9_<>,]*" ) and
432
+ result = "Dubious member name \"" + name + "\" in " + pred + " model."
433
+ or
434
+ not signature .regexpMatch ( "|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)" ) and
435
+ result = "Dubious signature \"" + signature + "\" in " + pred + " model."
436
+ or
437
+ not ext .regexpMatch ( "|Attribute" ) and
438
+ result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
439
+ or
440
+ not provenance = [ "manual" , "generated" ] and
441
+ result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
388
442
)
389
443
}
444
+
445
+ /** Holds if some row in a CSV-based flow model appears to contain typos. */
446
+ query predicate invalidModelRow ( string msg ) {
447
+ msg =
448
+ [
449
+ getInvalidModelSignature ( ) , getInvalidModelInput ( ) , getInvalidModelOutput ( ) ,
450
+ getInvalidModelSubtype ( ) , getInvalidModelColumnCount ( ) , getInvalidModelKind ( )
451
+ ]
452
+ }
390
453
}
391
454
392
455
private predicate elementSpec (
393
456
string namespace , string type , boolean subtypes , string name , string signature , string ext
394
457
) {
395
- sourceModel ( namespace , type , subtypes , name , signature , ext , _, _, _) or
396
- sinkModel ( namespace , type , subtypes , name , signature , ext , _, _, _) or
458
+ sourceModel ( namespace , type , subtypes , name , signature , ext , _, _, _)
459
+ or
460
+ sinkModel ( namespace , type , subtypes , name , signature , ext , _, _, _)
461
+ or
397
462
summaryModel ( namespace , type , subtypes , name , signature , ext , _, _, _, _)
463
+ or
464
+ negativeSummaryModel ( namespace , type , name , signature , _) and ext = "" and subtypes = false
398
465
}
399
466
400
467
private predicate elementSpec (
@@ -508,7 +575,7 @@ private Element interpretElement0(
508
575
)
509
576
}
510
577
511
- /** Gets the source/sink/summary element corresponding to the supplied parameters. */
578
+ /** Gets the source/sink/summary/negativesummary element corresponding to the supplied parameters. */
512
579
Element interpretElement (
513
580
string namespace , string type , boolean subtypes , string name , string signature , string ext
514
581
) {
0 commit comments