Skip to content

Commit ef2215d

Browse files
authored
Merge pull request github#18303 from michaelnebel/refactorlibrarylocations
C#: Move external api declarations to the library pack.
2 parents 66b2b5d + 1ef5b59 commit ef2215d

File tree

13 files changed

+199
-185
lines changed

13 files changed

+199
-185
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/** Provides classes and predicates related to handling APIs from external libraries. */
2+
3+
private import csharp
4+
private import semmle.code.csharp.dispatch.Dispatch
5+
private import semmle.code.csharp.dataflow.FlowSummary
6+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
7+
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
8+
private import semmle.code.csharp.dataflow.internal.ExternalFlow
9+
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10+
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
11+
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
12+
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
13+
private import TestLibrary
14+
15+
/** Holds if the given callable is not worth supporting. */
16+
private predicate isUninteresting(Callable c) {
17+
c.getDeclaringType() instanceof TestLibrary
18+
or
19+
c.(Constructor).isParameterless()
20+
or
21+
// The data flow library uses read/store steps for properties, so we don't need to model them,
22+
// if both a getter and a setter exist.
23+
c.(Accessor).getDeclaration().(Property).isReadWrite()
24+
}
25+
26+
/**
27+
* An external API from either the C# Standard Library or a 3rd party library.
28+
*/
29+
class ExternalApi extends Callable {
30+
ExternalApi() {
31+
this.isUnboundDeclaration() and
32+
this.fromLibrary() and
33+
this.(Modifiable).isEffectivelyPublic() and
34+
not isUninteresting(this)
35+
}
36+
37+
/**
38+
* Gets the unbound type, name and parameter types of this API.
39+
*/
40+
bindingset[this]
41+
private string getSignature() {
42+
result =
43+
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" +
44+
parameterQualifiedTypeNamesToString(this) + ")"
45+
}
46+
47+
/**
48+
* Gets the namespace of this API.
49+
*/
50+
bindingset[this]
51+
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
52+
53+
/**
54+
* Gets the namespace and signature of this API.
55+
*/
56+
bindingset[this]
57+
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
58+
59+
/** Gets a node that is an input to a call to this API. */
60+
private ArgumentNode getAnInput() {
61+
result
62+
.getCall()
63+
.(DataFlowDispatch::NonDelegateDataFlowCall)
64+
.getATarget(_)
65+
.getUnboundDeclaration() = this
66+
}
67+
68+
/** Gets a node that is an output from a call to this API. */
69+
private DataFlow::Node getAnOutput() {
70+
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
71+
dc.getDispatchCall().getCall() = c and
72+
c.getTarget().getUnboundDeclaration() = this
73+
|
74+
result = DataFlowDispatch::getAnOutNode(dc, _)
75+
)
76+
}
77+
78+
/** Holds if this API has a supported summary. */
79+
pragma[nomagic]
80+
predicate hasSummary() {
81+
this instanceof SummarizedCallable
82+
or
83+
defaultAdditionalTaintStep(this.getAnInput(), _, _)
84+
}
85+
86+
/** Holds if this API is a known source. */
87+
pragma[nomagic]
88+
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
89+
90+
/** Holds if this API is a known sink. */
91+
pragma[nomagic]
92+
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
93+
94+
/** Holds if this API is a known neutral. */
95+
pragma[nomagic]
96+
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
97+
98+
/**
99+
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
100+
* recognized source, sink or neutral or it has a flow summary.
101+
*/
102+
predicate isSupported() {
103+
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
104+
}
105+
}
106+
107+
/**
108+
* Gets the nested name of the type `t`.
109+
*
110+
* If the type is not a nested type, the result is the same as \`getName()\`.
111+
* Otherwise the name of the nested type is prefixed with a \`+\` and appended to
112+
* the name of the enclosing type, which might be a nested type as well.
113+
*/
114+
private string nestedName(Type t) {
115+
not exists(t.getDeclaringType().getUnboundDeclaration()) and
116+
result = t.getName()
117+
or
118+
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
119+
}
120+
121+
/**
122+
* Gets the limit for the number of results produced by a telemetry query.
123+
*/
124+
int resultLimit() { result = 100 }
125+
126+
/**
127+
* Holds if it is relevant to count usages of `api`.
128+
*/
129+
signature predicate relevantApi(ExternalApi api);
130+
131+
/**
132+
* Given a predicate to count relevant API usages, this module provides a predicate
133+
* for restricting the number or returned results based on a certain limit.
134+
*/
135+
module Results<relevantApi/1 getRelevantUsages> {
136+
private int getUsages(string apiName) {
137+
result =
138+
strictcount(Call c, ExternalApi api |
139+
c.getTarget().getUnboundDeclaration() = api and
140+
apiName = api.getApiName() and
141+
getRelevantUsages(api) and
142+
c.fromSource()
143+
)
144+
}
145+
146+
private int getOrder(string apiName) {
147+
apiName =
148+
rank[result](string name, int usages |
149+
usages = getUsages(name)
150+
|
151+
name order by usages desc, name
152+
)
153+
}
154+
155+
/**
156+
* Holds if there exists an API with `apiName` that is being used `usages` times
157+
* and if it is in the top results (guarded by resultLimit).
158+
*/
159+
predicate restrict(string apiName, int usages) {
160+
usages = getUsages(apiName) and
161+
getOrder(apiName) <= resultLimit()
162+
}
163+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/** Provides classes and predicates related to handling test libraries. */
2+
3+
private import csharp
4+
5+
pragma[nomagic]
6+
private predicate isTestNamespace(Namespace ns) {
7+
ns.getFullName()
8+
.matches([
9+
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
10+
])
11+
}
12+
13+
/**
14+
* A test library.
15+
*/
16+
class TestLibrary extends RefType {
17+
TestLibrary() { isTestNamespace(this.getNamespace()) }
18+
}
Lines changed: 3 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,4 @@
1-
/** Provides classes and predicates related to handling APIs from external libraries. */
1+
// Use `semmle.code.csharp.telemetry.ExternalApi` instead.
2+
deprecated module;
23

3-
private import csharp
4-
private import semmle.code.csharp.dispatch.Dispatch
5-
private import semmle.code.csharp.dataflow.FlowSummary
6-
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
7-
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
8-
private import semmle.code.csharp.dataflow.internal.ExternalFlow
9-
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10-
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
11-
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
12-
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
13-
private import TestLibrary
14-
15-
/** Holds if the given callable is not worth supporting. */
16-
private predicate isUninteresting(Callable c) {
17-
c.getDeclaringType() instanceof TestLibrary
18-
or
19-
c.(Constructor).isParameterless()
20-
or
21-
// The data flow library uses read/store steps for properties, so we don't need to model them,
22-
// if both a getter and a setter exist.
23-
c.(Accessor).getDeclaration().(Property).isReadWrite()
24-
}
25-
26-
/**
27-
* An external API from either the C# Standard Library or a 3rd party library.
28-
*/
29-
class ExternalApi extends Callable {
30-
ExternalApi() {
31-
this.isUnboundDeclaration() and
32-
this.fromLibrary() and
33-
this.(Modifiable).isEffectivelyPublic() and
34-
not isUninteresting(this)
35-
}
36-
37-
/**
38-
* Gets the unbound type, name and parameter types of this API.
39-
*/
40-
bindingset[this]
41-
private string getSignature() {
42-
result =
43-
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" +
44-
parameterQualifiedTypeNamesToString(this) + ")"
45-
}
46-
47-
/**
48-
* Gets the namespace of this API.
49-
*/
50-
bindingset[this]
51-
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
52-
53-
/**
54-
* Gets the namespace and signature of this API.
55-
*/
56-
bindingset[this]
57-
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
58-
59-
/** Gets a node that is an input to a call to this API. */
60-
private ArgumentNode getAnInput() {
61-
result
62-
.getCall()
63-
.(DataFlowDispatch::NonDelegateDataFlowCall)
64-
.getATarget(_)
65-
.getUnboundDeclaration() = this
66-
}
67-
68-
/** Gets a node that is an output from a call to this API. */
69-
private DataFlow::Node getAnOutput() {
70-
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
71-
dc.getDispatchCall().getCall() = c and
72-
c.getTarget().getUnboundDeclaration() = this
73-
|
74-
result = DataFlowDispatch::getAnOutNode(dc, _)
75-
)
76-
}
77-
78-
/** Holds if this API has a supported summary. */
79-
pragma[nomagic]
80-
predicate hasSummary() {
81-
this instanceof SummarizedCallable
82-
or
83-
defaultAdditionalTaintStep(this.getAnInput(), _, _)
84-
}
85-
86-
/** Holds if this API is a known source. */
87-
pragma[nomagic]
88-
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
89-
90-
/** Holds if this API is a known sink. */
91-
pragma[nomagic]
92-
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
93-
94-
/** Holds if this API is a known neutral. */
95-
pragma[nomagic]
96-
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
97-
98-
/**
99-
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
100-
* recognized source, sink or neutral or it has a flow summary.
101-
*/
102-
predicate isSupported() {
103-
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
104-
}
105-
}
106-
107-
/**
108-
* Gets the nested name of the type `t`.
109-
*
110-
* If the type is not a nested type, the result is the same as \`getName()\`.
111-
* Otherwise the name of the nested type is prefixed with a \`+\` and appended to
112-
* the name of the enclosing type, which might be a nested type as well.
113-
*/
114-
private string nestedName(Type t) {
115-
not exists(t.getDeclaringType().getUnboundDeclaration()) and
116-
result = t.getName()
117-
or
118-
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
119-
}
120-
121-
/**
122-
* Gets the limit for the number of results produced by a telemetry query.
123-
*/
124-
int resultLimit() { result = 100 }
125-
126-
/**
127-
* Holds if it is relevant to count usages of `api`.
128-
*/
129-
signature predicate relevantApi(ExternalApi api);
130-
131-
/**
132-
* Given a predicate to count relevant API usages, this module provides a predicate
133-
* for restricting the number or returned results based on a certain limit.
134-
*/
135-
module Results<relevantApi/1 getRelevantUsages> {
136-
private int getUsages(string apiName) {
137-
result =
138-
strictcount(Call c, ExternalApi api |
139-
c.getTarget().getUnboundDeclaration() = api and
140-
apiName = api.getApiName() and
141-
getRelevantUsages(api) and
142-
c.fromSource()
143-
)
144-
}
145-
146-
private int getOrder(string apiName) {
147-
apiName =
148-
rank[result](string name, int usages |
149-
usages = getUsages(name)
150-
|
151-
name order by usages desc, name
152-
)
153-
}
154-
155-
/**
156-
* Holds if there exists an API with `apiName` that is being used `usages` times
157-
* and if it is in the top results (guarded by resultLimit).
158-
*/
159-
predicate restrict(string apiName, int usages) {
160-
usages = getUsages(apiName) and
161-
getOrder(apiName) <= resultLimit()
162-
}
163-
}
4+
import semmle.code.csharp.telemetry.ExternalApi

csharp/ql/src/Telemetry/ExternalLibraryUsage.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
private import csharp
1010
private import semmle.code.csharp.dispatch.Dispatch
11-
private import ExternalApi
11+
private import semmle.code.csharp.telemetry.ExternalApi
1212

1313
private predicate getRelevantUsages(string namespace, int usages) {
1414
usages =

csharp/ql/src/Telemetry/SupportedExternalApis.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
private import csharp
1010
private import semmle.code.csharp.dispatch.Dispatch
11-
private import ExternalApi
11+
private import semmle.code.csharp.telemetry.ExternalApi
1212

1313
private predicate relevant(ExternalApi api) { api.isSupported() }
1414

csharp/ql/src/Telemetry/SupportedExternalSinks.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
private import csharp
1010
private import semmle.code.csharp.dispatch.Dispatch
11-
private import ExternalApi
11+
private import semmle.code.csharp.telemetry.ExternalApi
1212

1313
private predicate relevant(ExternalApi api) { api.isSink() }
1414

csharp/ql/src/Telemetry/SupportedExternalSources.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
private import csharp
1010
private import semmle.code.csharp.dispatch.Dispatch
11-
private import ExternalApi
11+
private import semmle.code.csharp.telemetry.ExternalApi
1212

1313
private predicate relevant(ExternalApi api) { api.isSource() }
1414

csharp/ql/src/Telemetry/SupportedExternalTaint.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
private import csharp
1010
private import semmle.code.csharp.dispatch.Dispatch
11-
private import ExternalApi
11+
private import semmle.code.csharp.telemetry.ExternalApi
1212

1313
private predicate relevant(ExternalApi api) { api.hasSummary() }
1414

0 commit comments

Comments
 (0)