Skip to content

Commit 7e8578a

Browse files
committed
Refactor data extensions editor queries to reduce duplication
This refactors the data extensions editor queries to use a new `AutomodelVsCode` module. This module is based on the `ExternalApi` module, but is more general and can be used for retrieving public methods from the source as well. The actual conditions are now in the queries themselves. This reduces the duplicated module in the framework mode query and will mean that when we update the `ExternalApi` module, we will just have to port it to the `AutomodelVsCode` module, and not to the `ExternalApi` and a separate framework mode query.
1 parent aa4d3f4 commit 7e8578a

File tree

3 files changed

+59
-317
lines changed

3 files changed

+59
-317
lines changed

extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts

Lines changed: 28 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ export const fetchExternalApisQuery: Query = {
99
* @id cs/telemetry/fetch-external-apis
1010
*/
1111
12-
import csharp
13-
import ExternalApi
14-
15-
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
12+
private import csharp
13+
private import AutomodelVsCode
1614
17-
private boolean isSupported(ExternalApi api) {
18-
api.isSupported() and result = true
19-
or
20-
not api.isSupported() and
21-
result = false
15+
class ExternalApi extends CallableMethod {
16+
ExternalApi() {
17+
this.isUnboundDeclaration() and
18+
this.fromLibrary() and
19+
this.(Modifiable).isEffectivelyPublic()
20+
}
2221
}
2322
23+
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
24+
2425
from ExternalApi api, string apiName, boolean supported, Call usage
2526
where
2627
apiName = api.getApiName() and
@@ -29,142 +30,30 @@ where
2930
select usage, apiName, supported.toString(), "supported", api.getFile().getBaseName(), "library"
3031
`,
3132
frameworkModeQuery: `/**
32-
* @name Usage of APIs coming from external libraries
33-
* @description A list of 3rd party APIs used in the codebase.
33+
* @name Public methods
34+
* @description A list of APIs callable by consumers. Excludes test and generated code.
3435
* @tags telemetry
3536
* @kind problem
36-
* @id cs/telemetry/fetch-external-apis
37+
* @id cs/telemetry/fetch-public-methods
3738
*/
3839
3940
private import csharp
40-
private import cil
4141
private import dotnet
42-
private import semmle.code.csharp.dispatch.Dispatch
43-
private import semmle.code.csharp.dataflow.ExternalFlow
44-
private import semmle.code.csharp.dataflow.FlowSummary
45-
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
46-
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
47-
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
48-
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
49-
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
50-
private import semmle.code.csharp.security.dataflow.flowsources.Remote
51-
52-
pragma[nomagic]
53-
private predicate isTestNamespace(Namespace ns) {
54-
ns.getFullName()
55-
.matches([
56-
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
57-
])
58-
}
59-
60-
/**
61-
* A test library.
62-
*/
63-
class TestLibrary extends RefType {
64-
TestLibrary() { isTestNamespace(this.getNamespace()) }
65-
}
66-
67-
/** Holds if the given callable is not worth supporting. */
68-
private predicate isUninteresting(DotNet::Callable c) {
69-
c.getDeclaringType() instanceof TestLibrary or
70-
c.(Constructor).isParameterless() or
71-
c.getDeclaringType() instanceof AnonymousClass
72-
}
73-
74-
class PublicMethod extends DotNet::Member {
75-
PublicMethod() { this.isPublic() and not isUninteresting(this) }
76-
77-
/**
78-
* Gets the unbound type, name and parameter types of this API.
79-
*/
80-
bindingset[this]
81-
private string getSignature() {
82-
result =
83-
this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" +
84-
parameterQualifiedTypeNamesToString(this) + ")"
85-
}
86-
87-
/**
88-
* Gets the namespace of this API.
89-
*/
90-
bindingset[this]
91-
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
92-
93-
/**
94-
* Gets the namespace and signature of this API.
95-
*/
96-
bindingset[this]
97-
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
98-
99-
/** Gets a node that is an input to a call to this API. */
100-
private ArgumentNode getAnInput() {
101-
result
102-
.getCall()
103-
.(DataFlowDispatch::NonDelegateDataFlowCall)
104-
.getATarget(_)
105-
.getUnboundDeclaration() = this
106-
}
107-
108-
/** Gets a node that is an output from a call to this API. */
109-
private DataFlow::Node getAnOutput() {
110-
exists(
111-
Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret
112-
|
113-
dc.getDispatchCall().getCall() = c and
114-
c.getTarget().getUnboundDeclaration() = this
115-
|
116-
result = ret.getAnOutNode(dc)
117-
)
118-
}
119-
120-
/** Holds if this API has a supported summary. */
121-
pragma[nomagic]
122-
predicate hasSummary() {
123-
this instanceof SummarizedCallable
124-
or
125-
defaultAdditionalTaintStep(this.getAnInput(), _)
126-
}
127-
128-
/** Holds if this API is a known source. */
129-
pragma[nomagic]
130-
predicate isSource() {
131-
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
132-
}
133-
134-
/** Holds if this API is a known sink. */
135-
pragma[nomagic]
136-
predicate isSink() { sinkNode(this.getAnInput(), _) }
137-
138-
/** Holds if this API is a known neutral. */
139-
pragma[nomagic]
140-
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
141-
142-
/**
143-
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
144-
* recognized source, sink or neutral or it has a flow summary.
145-
*/
146-
predicate isSupported() {
147-
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
148-
}
149-
}
42+
private import AutomodelVsCode
15043
151-
private boolean isSupported(PublicMethod publicMethod) {
152-
publicMethod.isSupported() and result = true
153-
or
154-
not publicMethod.isSupported() and
155-
result = false
44+
class PublicMethod extends CallableMethod {
45+
PublicMethod() { this.fromSource() }
15646
}
15747
15848
from PublicMethod publicMethod, string apiName, boolean supported
15949
where
16050
apiName = publicMethod.getApiName() and
161-
publicMethod.getDeclaringType().fromSource() and
16251
supported = isSupported(publicMethod)
16352
select publicMethod, apiName, supported.toString(), "supported",
16453
publicMethod.getFile().getBaseName(), "library"
16554
`,
16655
dependencies: {
167-
"ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */
56+
"AutomodelVsCode.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
16857
16958
private import csharp
17059
private import dotnet
@@ -194,18 +83,17 @@ class TestLibrary extends RefType {
19483
}
19584
19685
/** Holds if the given callable is not worth supporting. */
197-
private predicate isUninteresting(DotNet::Callable c) {
86+
private predicate isUninteresting(DotNet::Declaration c) {
19887
c.getDeclaringType() instanceof TestLibrary or
199-
c.(Constructor).isParameterless()
88+
c.(Constructor).isParameterless() or
89+
c.getDeclaringType() instanceof AnonymousClass
20090
}
20191
20292
/**
203-
* An external API from either the C# Standard Library or a 3rd party library.
93+
* An callable method from either the C# Standard Library, a 3rd party library, or from the source.
20494
*/
205-
class ExternalApi extends DotNet::Callable {
206-
ExternalApi() {
207-
this.isUnboundDeclaration() and
208-
this.fromLibrary() and
95+
class CallableMethod extends DotNet::Declaration {
96+
CallableMethod() {
20997
this.(Modifiable).isEffectivelyPublic() and
21098
not isUninteresting(this)
21199
}
@@ -284,47 +172,11 @@ class ExternalApi extends DotNet::Callable {
284172
}
285173
}
286174
287-
/**
288-
* Gets the limit for the number of results produced by a telemetry query.
289-
*/
290-
int resultLimit() { result = 1000 }
291-
292-
/**
293-
* Holds if it is relevant to count usages of \`api\`.
294-
*/
295-
signature predicate relevantApi(ExternalApi api);
296-
297-
/**
298-
* Given a predicate to count relevant API usages, this module provides a predicate
299-
* for restricting the number or returned results based on a certain limit.
300-
*/
301-
module Results<relevantApi/1 getRelevantUsages> {
302-
private int getUsages(string apiName) {
303-
result =
304-
strictcount(Call c, ExternalApi api |
305-
c.getTarget().getUnboundDeclaration() = api and
306-
apiName = api.getApiName() and
307-
getRelevantUsages(api)
308-
)
309-
}
310-
311-
private int getOrder(string apiName) {
312-
apiName =
313-
rank[result](string name, int usages |
314-
usages = getUsages(name)
315-
|
316-
name order by usages desc, name
317-
)
318-
}
319-
320-
/**
321-
* Holds if there exists an API with \`apiName\` that is being used \`usages\` times
322-
* and if it is in the top results (guarded by resultLimit).
323-
*/
324-
predicate restrict(string apiName, int usages) {
325-
usages = getUsages(apiName) and
326-
getOrder(apiName) <= resultLimit()
327-
}
175+
boolean isSupported(CallableMethod callableMethod) {
176+
callableMethod.isSupported() and result = true
177+
or
178+
not callableMethod.isSupported() and
179+
result = false
328180
}
329181
`,
330182
},

0 commit comments

Comments
 (0)