Skip to content

Commit e607c51

Browse files
committed
C#: Initial implementation of csv printing in FlowSummaries test
1 parent f9729bc commit e607c51

File tree

6 files changed

+111
-20
lines changed

6 files changed

+111
-20
lines changed

csharp/ql/lib/semmle/code/csharp/Member.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ class Parameterizable extends DotNet::Parameterizable, Declaration, @parameteriz
383383
override Parameter getParameter(int i) { params(result, _, _, i, _, this, _) }
384384

385385
/**
386-
* Gets the name of this parameter followed by its type, possibly prefixed
386+
* Gets the type of the parameter, possibly prefixed
387387
* with `out`, `ref`, or `params`, where appropriate.
388388
*/
389389
private string parameterTypeToString(int i) {

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

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,38 @@ module Public {
127127
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
128128
}
129129

130+
private predicate noComponentSpecificCsv(SummaryComponent sc) {
131+
not exists(getComponentSpecificCsv(sc))
132+
}
133+
134+
/** Gets a textual representation of this component used for flow summaries */
135+
private string getComponentCsv(SummaryComponent sc) {
136+
result = getComponentSpecificCsv(sc)
137+
or
138+
noComponentSpecificCsv(sc) and
139+
(
140+
exists(int i | sc = TParameterSummaryComponent(i) and result = "Parameter[" + i + "]")
141+
or
142+
exists(int i | sc = TArgumentSummaryComponent(i) and result = "Argument[" + i + "]")
143+
or
144+
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
145+
)
146+
}
147+
148+
/** Gets a textual representation of this stack used for flow summaries. */
149+
string getComponentStackCsv(SummaryComponentStack stack) {
150+
exists(SummaryComponent head, SummaryComponentStack tail |
151+
head = stack.head() and
152+
tail = stack.tail() and
153+
result = getComponentCsv(head) + " of " + getComponentStackCsv(tail)
154+
)
155+
or
156+
exists(SummaryComponent c |
157+
stack = TSingletonSummaryComponentStack(c) and
158+
result = getComponentCsv(c)
159+
)
160+
}
161+
130162
/**
131163
* A class that exists for QL technical reasons only (the IPA type used
132164
* to represent component stacks needs to be bounded).
@@ -970,18 +1002,31 @@ module Private {
9701002
module TestOutput {
9711003
/** A flow summary to include in the `summary/3` query predicate. */
9721004
abstract class RelevantSummarizedCallable extends SummarizedCallable {
973-
/** Gets the string representation of this callable used by `summary/3`. */
974-
string getFullString() { result = this.toString() }
1005+
/** Gets the string representation of this callable used by `summary/1`. */
1006+
abstract string getCallableCsv();
9751007
}
9761008

977-
/** A query predicate for outputting flow summaries in QL tests. */
978-
query predicate summary(string callable, string flow, boolean preservesValue) {
1009+
/** Render the kind in the format used in flow summaries. */
1010+
private string renderKind(boolean preservesValue) {
1011+
preservesValue = true and result = "value"
1012+
or
1013+
preservesValue = false and result = "taint"
1014+
}
1015+
1016+
/**
1017+
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
1018+
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind",
1019+
* ext is hardcoded to empty
1020+
*/
1021+
query predicate summary(string csv) {
9791022
exists(
980-
RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output
1023+
RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output,
1024+
boolean preservesValue
9811025
|
982-
callable = c.getFullString() and
9831026
c.propagatesFlow(input, output, preservesValue) and
984-
flow = input + " -> " + output
1027+
csv =
1028+
c.getCallableCsv() + ";;" + getComponentStackCsv(input) + ";" +
1029+
getComponentStackCsv(output) + ";" + renderKind(preservesValue)
9851030
)
9861031
}
9871032
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ SummaryComponent interpretComponentSpecific(string c) {
138138
)
139139
}
140140

141+
/** Gets the textual representation of the content in the format used for flow summaries */
142+
private string getContentSpecificCsv(Content c) {
143+
c = TElementContent() and result = "Element"
144+
or
145+
exists(Field f | c = TFieldContent(f) and result = "Field[" + f.getQualifiedName() + "]")
146+
or
147+
exists(Property p | c = TPropertyContent(p) and result = "Property[" + p.getQualifiedName() + "]")
148+
}
149+
150+
/** Gets the textual representation of a summary component in the format used for flow summaries */
151+
string getComponentSpecificCsv(SummaryComponent sc) {
152+
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecificCsv(c))
153+
}
154+
141155
class SourceOrSinkElement = Element;
142156

143157
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import semmle.code.csharp.dataflow.FlowSummary
2-
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
1+
import shared.FlowSummaries
32

4-
private class IncludeSummarizedCallable extends RelevantSummarizedCallable {
5-
IncludeSummarizedCallable() { this instanceof SummarizedCallable }
6-
7-
override string getFullString() { result = this.(Callable).getQualifiedNameWithTypes() }
3+
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
4+
IncludeAllSummarizedCallable() { this instanceof SummarizedCallable }
85
}
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import semmle.code.csharp.dataflow.FlowSummary
2-
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
31
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework
2+
import shared.FlowSummaries
43

5-
private class IncludeSummarizedCallable extends RelevantSummarizedCallable {
6-
IncludeSummarizedCallable() { this instanceof EFSummarizedCallable }
7-
8-
override string getFullString() { result = this.(Callable).getQualifiedNameWithTypes() }
4+
private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable {
5+
IncludeEFSummarizedCallable() { this instanceof EFSummarizedCallable }
96
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import semmle.code.csharp.dataflow.FlowSummary
2+
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
3+
4+
abstract class IncludeSummarizedCallable extends RelevantSummarizedCallable {
5+
/** Gets the qualified parameter types of this callable as a comma-separated string */
6+
private string parameterQualifiedTypeNamesToString() {
7+
result =
8+
concat(int i |
9+
exists(this.getParameter(i))
10+
|
11+
this.getParameter(i).getType().getQualifiedName(), ", " order by i
12+
)
13+
}
14+
15+
/* Gets a string representation of the input type signature of callable */
16+
private string getCallableSignature() {
17+
result = "(" + parameterQualifiedTypeNamesToString() + ")"
18+
}
19+
20+
/* Gets a string representing, whether the declaring type is an interface */
21+
private string getCallableOverride() {
22+
if
23+
this.getDeclaringType() instanceof Interface or
24+
this.(Modifiable).isAbstract()
25+
then result = "true"
26+
else result = "false"
27+
}
28+
29+
/** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */
30+
final override string getCallableCsv() {
31+
exists(string namespace, string type |
32+
this.getDeclaringType().hasQualifiedName(namespace, type) and
33+
result =
34+
namespace + ";" + type + ";" + this.getCallableOverride() + ";" + this.getName() + ";" +
35+
this.getCallableSignature()
36+
)
37+
}
38+
}

0 commit comments

Comments
 (0)