55 *
66 * The CSV specification has the following columns:
77 * - Sources:
8- * `namespace; type; subtypes; name; signature; ext; output; kind`
8+ * `namespace; type; subtypes; name; signature; ext; output; kind; provenance `
99 * - Sinks:
10- * `namespace; type; subtypes; name; signature; ext; input; kind`
10+ * `namespace; type; subtypes; name; signature; ext; input; kind; provenance `
1111 * - 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`
1415 * The interpretation of a row is similar to API-graphs with a left-to-right
1516 * reading.
1617 * 1. The `namespace` column selects a namespace.
@@ -163,11 +164,27 @@ class SummaryModelCsv extends Unit {
163164 abstract predicate row ( string row ) ;
164165}
165166
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 ) }
167179
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 ) }
169182
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 ) }
171188
172189/** Holds if a source model exists for the given parameters. */
173190predicate sourceModel (
@@ -230,6 +247,20 @@ predicate summaryModel(
230247 )
231248}
232249
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+
233264private predicate relevantNamespace ( string namespace ) {
234265 sourceModel ( namespace , _, _, _, _, _, _, _, _) or
235266 sinkModel ( namespace , _, _, _, _, _, _, _, _) or
@@ -286,38 +317,7 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
286317
287318/** Provides a query predicate to check the CSV data for validation errors. */
288319module 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 ( ) {
321321 exists ( string pred , AccessPath input , string part |
322322 sinkModel ( _, _, _, _, _, _, input , _, _) and pred = "sink"
323323 or
@@ -332,9 +332,11 @@ module CsvValidation {
332332 part = input .getToken ( _) and
333333 parseParam ( part , _)
334334 ) and
335- msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
335+ result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
336336 )
337- or
337+ }
338+
339+ private string getInvalidModelOutput ( ) {
338340 exists ( string pred , string output , string part |
339341 sourceModel ( _, _, _, _, _, _, output , _, _) and pred = "source"
340342 or
@@ -343,58 +345,123 @@ module CsvValidation {
343345 invalidSpecComponent ( output , part ) and
344346 not part = "" and
345347 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."
347357 )
348358 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 ( ) {
349390 exists ( string pred , string row , int expect |
350391 sourceModel ( row ) and expect = 9 and pred = "source"
351392 or
352393 sinkModel ( row ) and expect = 9 and pred = "sink"
353394 or
354395 summaryModel ( row ) and expect = 10 and pred = "summary"
396+ or
397+ negativeSummaryModel ( row ) and expect = 5 and pred = "negative summary"
355398 |
356399 exists ( int cols |
357400 cols = 1 + max ( int n | exists ( row .splitAt ( ";" , n ) ) ) and
358401 cols != expect and
359- msg =
402+ result =
360403 "Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
361404 " in " + row + "."
362405 )
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- )
369406 )
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."
388442 )
389443 }
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+ }
390453}
391454
392455private predicate elementSpec (
393456 string namespace , string type , boolean subtypes , string name , string signature , string ext
394457) {
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
397462 summaryModel ( namespace , type , subtypes , name , signature , ext , _, _, _, _)
463+ or
464+ negativeSummaryModel ( namespace , type , name , signature , _) and ext = "" and subtypes = false
398465}
399466
400467private predicate elementSpec (
@@ -508,7 +575,7 @@ private Element interpretElement0(
508575 )
509576}
510577
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. */
512579Element interpretElement (
513580 string namespace , string type , boolean subtypes , string name , string signature , string ext
514581) {
0 commit comments