@@ -33,147 +33,157 @@ select apiName, supported, usage
3333 dependencies : {
3434 "ExternalApi.qll" : `/** Provides classes and predicates related to handling APIs from external libraries. */
3535
36- private import java
37- private import semmle.code.java.dataflow.DataFlow
38- private import semmle.code.java.dataflow.ExternalFlow
39- private import semmle.code.java.dataflow.FlowSources
40- private import semmle.code.java.dataflow.FlowSummary
41- private import semmle.code.java.dataflow.internal.DataFlowPrivate
42- private import semmle.code.java.dataflow.TaintTracking
43-
44- pragma[nomagic]
45- private predicate isTestPackage(Package p) {
46- p.getName()
47- .matches([
48- "org.junit%", "junit.%", "org.mockito%", "org.assertj%",
49- "com.github.tomakehurst.wiremock%", "org.hamcrest%", "org.springframework.test.%",
50- "org.springframework.mock.%", "org.springframework.boot.test.%", "reactor.test%",
51- "org.xmlunit%", "org.testcontainers.%", "org.opentest4j%", "org.mockserver%",
52- "org.powermock%", "org.skyscreamer.jsonassert%", "org.rnorth.visibleassertions",
53- "org.openqa.selenium%", "com.gargoylesoftware.htmlunit%", "org.jboss.arquillian.testng%",
54- "org.testng%"
55- ])
56- }
57-
58- /**
59- * A test library.
60- */
61- private class TestLibrary extends RefType {
62- TestLibrary() { isTestPackage(this.getPackage()) }
63- }
64-
65- private string containerAsJar(Container container) {
66- if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"
67- }
68-
69- /** Holds if the given callable is not worth supporting. */
70- private predicate isUninteresting(Callable c) {
71- c.getDeclaringType() instanceof TestLibrary or
72- c.(Constructor).isParameterless()
73- }
74-
75- /**
76- * An external API from either the Standard Library or a 3rd party library.
77- */
78- class ExternalApi extends Callable {
79- ExternalApi() { not this.fromSource() and not isUninteresting(this) }
80-
81- /**
82- * Gets information about the external API in the form expected by the MaD modeling framework.
83- */
84- string getApiName() {
85- result =
86- this.getDeclaringType().getPackage() + "." + this.getDeclaringType().getSourceDeclaration() +
87- "#" + this.getName() + paramsString(this)
88- }
89-
90- /**
91- * Gets the jar file containing this API. Normalizes the Java Runtime to "rt.jar" despite the presence of modules.
92- */
93- string jarContainer() { result = containerAsJar(this.getCompilationUnit().getParentContainer*()) }
94-
95- /** Gets a node that is an input to a call to this API. */
96- private DataFlow::Node getAnInput() {
97- exists(Call call | call.getCallee().getSourceDeclaration() = this |
98- result.asExpr().(Argument).getCall() = call or
99- result.(ArgumentNode).getCall().asCall() = call
100- )
101- }
102-
103- /** Gets a node that is an output from a call to this API. */
104- private DataFlow::Node getAnOutput() {
105- exists(Call call | call.getCallee().getSourceDeclaration() = this |
106- result.asExpr() = call or
107- result.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).getCall().asCall() = call
108- )
109- }
110-
111- /** Holds if this API has a supported summary. */
112- pragma[nomagic]
113- predicate hasSummary() {
114- this = any(SummarizedCallable sc).asCallable() or
115- TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
116- }
117-
118- pragma[nomagic]
119- predicate isSource() {
120- this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
121- }
122-
123- /** Holds if this API is a known sink. */
124- pragma[nomagic]
125- predicate isSink() { sinkNode(this.getAnInput(), _) }
126-
127- /** Holds if this API is supported by existing CodeQL libraries, that is, it is either a recognized source or sink or has a flow summary. */
128- predicate isSupported() { this.hasSummary() or this.isSource() or this.isSink() }
129- }
130-
131- /** DEPRECATED: Alias for ExternalApi */
132- deprecated class ExternalAPI = ExternalApi;
133-
134- /**
135- * Gets the limit for the number of results produced by a telemetry query.
136- */
137- int resultLimit() { result = 1000 }
138-
139- /**
140- * Holds if it is relevant to count usages of \`api\`.
141- */
142- signature predicate relevantApi(ExternalApi api);
143-
144- /**
145- * Given a predicate to count relevant API usages, this module provides a predicate
146- * for restricting the number or returned results based on a certain limit.
147- */
148- module Results<relevantApi/1 getRelevantUsages> {
149- private int getUsages(string apiName) {
150- result =
151- strictcount(Call c, ExternalApi api |
152- c.getCallee().getSourceDeclaration() = api and
153- not c.getFile() instanceof GeneratedFile and
154- apiName = api.getApiName() and
155- getRelevantUsages(api)
156- )
157- }
158-
159- private int getOrder(string apiInfo) {
160- apiInfo =
161- rank[result](string info, int usages |
162- usages = getUsages(info)
163- |
164- info order by usages desc, info
165- )
166- }
167-
168- /**
169- * Holds if there exists an API with \`apiName\` that is being used \`usages\` times
170- * and if it is in the top results (guarded by resultLimit).
171- */
172- predicate restrict(string apiName, int usages) {
173- usages = getUsages(apiName) and
174- getOrder(apiName) <= resultLimit()
175- }
176- }
36+ private import java
37+ private import semmle.code.java.dataflow.DataFlow
38+ private import semmle.code.java.dataflow.ExternalFlow
39+ private import semmle.code.java.dataflow.FlowSources
40+ private import semmle.code.java.dataflow.FlowSummary
41+ private import semmle.code.java.dataflow.internal.DataFlowPrivate
42+ private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
43+ private import semmle.code.java.dataflow.TaintTracking
44+
45+ pragma[nomagic]
46+ private predicate isTestPackage(Package p) {
47+ p.getName()
48+ .matches([
49+ "org.junit%", "junit.%", "org.mockito%", "org.assertj%",
50+ "com.github.tomakehurst.wiremock%", "org.hamcrest%", "org.springframework.test.%",
51+ "org.springframework.mock.%", "org.springframework.boot.test.%", "reactor.test%",
52+ "org.xmlunit%", "org.testcontainers.%", "org.opentest4j%", "org.mockserver%",
53+ "org.powermock%", "org.skyscreamer.jsonassert%", "org.rnorth.visibleassertions",
54+ "org.openqa.selenium%", "com.gargoylesoftware.htmlunit%", "org.jboss.arquillian.testng%",
55+ "org.testng%"
56+ ])
57+ }
58+
59+ /**
60+ * A test library.
61+ */
62+ private class TestLibrary extends RefType {
63+ TestLibrary() { isTestPackage(this.getPackage()) }
64+ }
65+
66+ private string containerAsJar(Container container) {
67+ if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"
68+ }
69+
70+ /** Holds if the given callable is not worth supporting. */
71+ private predicate isUninteresting(Callable c) {
72+ c.getDeclaringType() instanceof TestLibrary or
73+ c.(Constructor).isParameterless()
74+ }
75+
76+ /**
77+ * An external API from either the Standard Library or a 3rd party library.
78+ */
79+ class ExternalApi extends Callable {
80+ ExternalApi() { not this.fromSource() and not isUninteresting(this) }
81+
82+ /**
83+ * Gets information about the external API in the form expected by the MaD modeling framework.
84+ */
85+ string getApiName() {
86+ result =
87+ this.getDeclaringType().getPackage() + "." + this.getDeclaringType().getSourceDeclaration() +
88+ "#" + this.getName() + paramsString(this)
89+ }
90+
91+ /**
92+ * Gets the jar file containing this API. Normalizes the Java Runtime to "rt.jar" despite the presence of modules.
93+ */
94+ string jarContainer() { result = containerAsJar(this.getCompilationUnit().getParentContainer*()) }
95+
96+ /** Gets a node that is an input to a call to this API. */
97+ private DataFlow::Node getAnInput() {
98+ exists(Call call | call.getCallee().getSourceDeclaration() = this |
99+ result.asExpr().(Argument).getCall() = call or
100+ result.(ArgumentNode).getCall().asCall() = call
101+ )
102+ }
103+
104+ /** Gets a node that is an output from a call to this API. */
105+ private DataFlow::Node getAnOutput() {
106+ exists(Call call | call.getCallee().getSourceDeclaration() = this |
107+ result.asExpr() = call or
108+ result.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).getCall().asCall() = call
109+ )
110+ }
111+
112+ /** Holds if this API has a supported summary. */
113+ pragma[nomagic]
114+ predicate hasSummary() {
115+ this = any(SummarizedCallable sc).asCallable() or
116+ TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
117+ }
118+
119+ pragma[nomagic]
120+ predicate isSource() {
121+ this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
122+ }
123+
124+ /** Holds if this API is a known sink. */
125+ pragma[nomagic]
126+ predicate isSink() { sinkNode(this.getAnInput(), _) }
127+
128+ /** Holds if this API is a known neutral. */
129+ pragma[nomagic]
130+ predicate isNeutral() { this = any(FlowSummaryImpl::Public::NeutralCallable nsc).asCallable() }
131+
132+ /**
133+ * Holds if this API is supported by existing CodeQL libraries, that is, it is either a
134+ * recognized source, sink or neutral or it has a flow summary.
135+ */
136+ predicate isSupported() {
137+ this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
138+ }
139+ }
140+
141+ /** DEPRECATED: Alias for ExternalApi */
142+ deprecated class ExternalAPI = ExternalApi;
143+
144+ /**
145+ * Gets the limit for the number of results produced by a telemetry query.
146+ */
147+ int resultLimit() { result = 1000 }
148+
149+ /**
150+ * Holds if it is relevant to count usages of \`api\`.
151+ */
152+ signature predicate relevantApi(ExternalApi api);
153+
154+ /**
155+ * Given a predicate to count relevant API usages, this module provides a predicate
156+ * for restricting the number or returned results based on a certain limit.
157+ */
158+ module Results<relevantApi/1 getRelevantUsages> {
159+ private int getUsages(string apiName) {
160+ result =
161+ strictcount(Call c, ExternalApi api |
162+ c.getCallee().getSourceDeclaration() = api and
163+ not c.getFile() instanceof GeneratedFile and
164+ apiName = api.getApiName() and
165+ getRelevantUsages(api)
166+ )
167+ }
168+
169+ private int getOrder(string apiInfo) {
170+ apiInfo =
171+ rank[result](string info, int usages |
172+ usages = getUsages(info)
173+ |
174+ info order by usages desc, info
175+ )
176+ }
177+
178+ /**
179+ * Holds if there exists an API with \`apiName\` that is being used \`usages\` times
180+ * and if it is in the top results (guarded by resultLimit).
181+ */
182+ predicate restrict(string apiName, int usages) {
183+ usages = getUsages(apiName) and
184+ getOrder(apiName) <= resultLimit()
185+ }
186+ }
177187` ,
178188 } ,
179189} ;
0 commit comments