Skip to content

Commit c514c88

Browse files
authored
Merge pull request #9867 from michaelnebel/csharp/nosummary
C#: Negative summaries (ie. no flow through)
2 parents 623d87a + a412c95 commit c514c88

File tree

53 files changed

+72374
-630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+72374
-630
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll

Lines changed: 137 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
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. */
173190
predicate 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+
233264
private 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. */
288319
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() {
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

392455
private 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

400467
private 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. */
512579
Element interpretElement(
513580
string namespace, string type, boolean subtypes, string name, string signature, string ext
514581
) {

csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ module Ssa {
173173
}
174174

175175
/**
176-
* Holds is this SSA definition is live at the end of basic block `bb`.
176+
* Holds if this SSA definition is live at the end of basic block `bb`.
177177
* That is, this definition reaches the end of basic block `bb`, at which
178178
* point it is still live, without crossing another SSA definition of the
179179
* same source variable.

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,18 +2129,37 @@ module Csv {
21292129
if isBaseCallableOrPrototype(c) then result = "true" else result = "false"
21302130
}
21312131

2132-
/** Computes the first 6 columns for CSV rows of `c`. */
2132+
private predicate partialModel(
2133+
DotNet::Callable c, string namespace, string type, string name, string parameters
2134+
) {
2135+
c.getDeclaringType().hasQualifiedName(namespace, type) and
2136+
c.hasQualifiedName(_, name) and
2137+
parameters = "(" + parameterQualifiedTypeNamesToString(c) + ")"
2138+
}
2139+
2140+
/** Computes the first 6 columns for positive CSV rows of `c`. */
21332141
string asPartialModel(DotNet::Callable c) {
2134-
exists(string namespace, string type, string name |
2135-
c.getDeclaringType().hasQualifiedName(namespace, type) and
2136-
c.hasQualifiedName(_, name) and
2142+
exists(string namespace, string type, string name, string parameters |
2143+
partialModel(c, namespace, type, name, parameters) and
21372144
result =
21382145
namespace + ";" //
21392146
+ type + ";" //
21402147
+ getCallableOverride(c) + ";" //
21412148
+ name + ";" //
2142-
+ "(" + parameterQualifiedTypeNamesToString(c) + ")" + ";" //
2149+
+ parameters + ";" //
21432150
+ /* ext + */ ";" //
21442151
)
21452152
}
2153+
2154+
/** Computes the first 4 columns for negative CSV rows of `c`. */
2155+
string asPartialNegativeModel(DotNet::Callable c) {
2156+
exists(string namespace, string type, string name, string parameters |
2157+
partialModel(c, namespace, type, name, parameters) and
2158+
result =
2159+
namespace + ";" //
2160+
+ type + ";" //
2161+
+ name + ";" //
2162+
+ parameters + ";" //
2163+
)
2164+
}
21462165
}

0 commit comments

Comments
 (0)