@@ -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+
2425from ExternalApi api, string apiName, boolean supported, Call usage
2526where
2627 apiName = api.getApiName() and
@@ -29,142 +30,30 @@ where
2930select 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
3940private import csharp
40- private import cil
4141private 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
15848from PublicMethod publicMethod, string apiName, boolean supported
15949where
16050 apiName = publicMethod.getApiName() and
161- publicMethod.getDeclaringType().fromSource() and
16251 supported = isSupported(publicMethod)
16352select 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
16958private import csharp
17059private 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