Skip to content

Commit 7db082f

Browse files
committed
Java: Add VS Code model editor queries
1 parent 3bf0d66 commit 7db082f

15 files changed

+248
-1
lines changed

java/ql/lib/semmle/code/java/dataflow/internal/ModelExclusions.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class TestLibrary extends RefType {
2525
}
2626

2727
/** Holds if the given file is a test file. */
28-
private predicate isInTestFile(File file) {
28+
predicate isInTestFile(File file) {
2929
file.getAbsolutePath().matches(["%/test/%", "%/guava-tests/%", "%/guava-testlib/%"]) and
3030
not file.getAbsolutePath().matches(["%/ql/test/%", "%/ql/automodel/test/%"]) // allows our test cases to work
3131
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/** Provides classes and predicates related to handling APIs for the VS Code extension. */
2+
3+
private import java
4+
private import semmle.code.java.dataflow.DataFlow
5+
private import semmle.code.java.dataflow.ExternalFlow
6+
private import semmle.code.java.dataflow.FlowSources
7+
private import semmle.code.java.dataflow.FlowSummary
8+
private import semmle.code.java.dataflow.internal.DataFlowPrivate
9+
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10+
private import semmle.code.java.dataflow.TaintTracking
11+
private import semmle.code.java.dataflow.internal.ModelExclusions
12+
13+
/** Holds if the given callable/method is not worth supporting. */
14+
private predicate isUninteresting(Callable c) {
15+
c.getDeclaringType() instanceof TestLibrary or
16+
c.(Constructor).isParameterless() or
17+
c.getDeclaringType() instanceof AnonymousClass
18+
}
19+
20+
/**
21+
* A callable method from either the Standard Library, a 3rd party library or from the source.
22+
*/
23+
class CallableMethod extends Callable {
24+
CallableMethod() { not isUninteresting(this) }
25+
26+
/**
27+
* Gets information about the external API in the form expected by the MaD modeling framework.
28+
*/
29+
string getApiName() {
30+
result =
31+
this.getDeclaringType().getPackage() + "." + this.getDeclaringType().nestedName() + "#" +
32+
this.getName() + paramsString(this)
33+
}
34+
35+
private string getJarName() {
36+
result = this.getCompilationUnit().getParentContainer*().(JarFile).getBaseName()
37+
}
38+
39+
private string getJarVersion() {
40+
result = this.getCompilationUnit().getParentContainer*().(JarFile).getSpecificationVersion()
41+
}
42+
43+
/**
44+
* Gets the jar file containing this API. Normalizes the Java Runtime to "rt.jar" despite the presence of modules.
45+
*/
46+
string jarContainer() {
47+
result = this.getJarName()
48+
or
49+
not exists(this.getJarName()) and result = "rt.jar"
50+
}
51+
52+
/**
53+
* Gets the version of the JAR file containing this API. Empty if no version is found in the JAR.
54+
*/
55+
string jarVersion() {
56+
result = this.getJarVersion()
57+
or
58+
not exists(this.getJarVersion()) and result = ""
59+
}
60+
61+
/** Gets a node that is an input to a call to this API. */
62+
private DataFlow::Node getAnInput() {
63+
exists(Call call | call.getCallee().getSourceDeclaration() = this |
64+
result.asExpr().(Argument).getCall() = call or
65+
result.(ArgumentNode).getCall().asCall() = call
66+
)
67+
}
68+
69+
/** Gets a node that is an output from a call to this API. */
70+
private DataFlow::Node getAnOutput() {
71+
exists(Call call | call.getCallee().getSourceDeclaration() = this |
72+
result.asExpr() = call or
73+
result.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).getCall().asCall() = call
74+
)
75+
}
76+
77+
/** Holds if this API has a supported summary. */
78+
pragma[nomagic]
79+
predicate hasSummary() {
80+
this = any(SummarizedCallable sc).asCallable() or
81+
TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
82+
}
83+
84+
pragma[nomagic]
85+
predicate isSource() {
86+
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
87+
}
88+
89+
/** Holds if this API is a known sink. */
90+
pragma[nomagic]
91+
predicate isSink() { sinkNode(this.getAnInput(), _) }
92+
93+
/** Holds if this API is a known neutral. */
94+
pragma[nomagic]
95+
predicate isNeutral() {
96+
exists(
97+
string namespace, string type, string name, string signature, string kind, string provenance
98+
|
99+
neutralModel(namespace, type, name, signature, kind, provenance) and
100+
this = interpretElement(namespace, type, false, name, signature, "")
101+
)
102+
}
103+
104+
/**
105+
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
106+
* recognized source, sink or neutral or it has a flow summary.
107+
*/
108+
predicate isSupported() {
109+
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
110+
}
111+
}
112+
113+
boolean isSupported(CallableMethod method) {
114+
method.isSupported() and result = true
115+
or
116+
not method.isSupported() and result = false
117+
}
118+
119+
string supportedType(CallableMethod method) {
120+
method.isSink() and result = "sink"
121+
or
122+
method.isSource() and result = "source"
123+
or
124+
method.hasSummary() and result = "summary"
125+
or
126+
method.isNeutral() and result = "neutral"
127+
or
128+
not method.isSupported() and result = ""
129+
}
130+
131+
string methodClassification(Call method) {
132+
isInTestFile(method.getLocation().getFile()) and result = "test"
133+
or
134+
method.getFile() instanceof GeneratedFile and result = "generated"
135+
or
136+
not isInTestFile(method.getLocation().getFile()) and
137+
not method.getFile() instanceof GeneratedFile and
138+
result = "source"
139+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @name Fetch model editor methods (application mode)
3+
* @description A list of 3rd party APIs used in the codebase. Excludes test and generated code.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id java/utils/modeleditor/fetch-application-mode-methods
7+
* @tags modeleditor fetch methods application-mode
8+
*/
9+
10+
private import java
11+
private import AutomodelVsCode
12+
13+
class ExternalApi extends CallableMethod {
14+
ExternalApi() { not this.fromSource() }
15+
}
16+
17+
private Call aUsage(ExternalApi api) { result.getCallee().getSourceDeclaration() = api }
18+
19+
from
20+
ExternalApi externalApi, string apiName, boolean supported, Call usage, string type,
21+
string classification
22+
where
23+
apiName = externalApi.getApiName() and
24+
supported = isSupported(externalApi) and
25+
usage = aUsage(externalApi) and
26+
type = supportedType(externalApi) and
27+
classification = methodClassification(usage)
28+
select usage, apiName, supported.toString(), "supported", externalApi.jarContainer(),
29+
externalApi.jarVersion(), type, "type", classification, "classification"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @name Fetch model editor methods (framework mode)
3+
* @description A list of APIs callable by consumers. Excludes test and generated code.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id java/utils/modeleditor/fetch-framework-mode-methods
7+
* @tags modeleditor fetch methods framework-mode
8+
*/
9+
10+
private import java
11+
private import semmle.code.java.dataflow.internal.ModelExclusions
12+
private import AutomodelVsCode
13+
14+
class PublicMethodFromSource extends CallableMethod, ModelApi { }
15+
16+
from PublicMethodFromSource publicMethod, string apiName, boolean supported, string type
17+
where
18+
apiName = publicMethod.getApiName() and
19+
supported = isSupported(publicMethod) and
20+
type = supportedType(publicMethod)
21+
select publicMethod, apiName, supported.toString(), "supported",
22+
publicMethod.getCompilationUnit().getParentContainer().getBaseName(), "library", type, "type",
23+
"unknown", "classification"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| com/github/codeql/test/NonPublicClass.java:5:5:5:28 | println(...) | java.io.PrintStream#println(String) | true | supported | rt.jar | | sink | type | source | classification |
2+
| com/github/codeql/test/PublicClass.java:7:5:7:27 | println(...) | java.io.PrintStream#println(String) | true | supported | rt.jar | | sink | type | source | classification |
3+
| com/github/codeql/test/PublicClass.java:11:5:11:27 | println(...) | java.io.PrintStream#println(String) | true | supported | rt.jar | | sink | type | source | classification |
4+
| com/github/codeql/test/PublicClass.java:15:5:15:45 | println(...) | java.io.PrintStream#println(Object) | true | supported | rt.jar | | sink | type | source | classification |
5+
| com/github/codeql/test/PublicClass.java:15:24:15:44 | get(...) | java.nio.file.Paths#get(String,String[]) | true | supported | rt.jar | | sink | type | source | classification |
6+
| com/github/codeql/test/PublicClass.java:15:24:15:44 | get(...) | java.nio.file.Paths#get(String,String[]) | true | supported | rt.jar | | summary | type | source | classification |
7+
| com/github/codeql/test/PublicClass.java:19:5:19:27 | println(...) | java.io.PrintStream#println(String) | true | supported | rt.jar | | sink | type | source | classification |
8+
| com/github/codeql/test/PublicInterface.java:7:7:7:29 | println(...) | java.io.PrintStream#println(String) | true | supported | rt.jar | | sink | type | source | classification |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/FetchApplicationModeMethods.ql
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| com/github/codeql/test/PublicClass.java:6:15:6:19 | stuff | com.github.codeql.test.PublicClass#stuff(String) | false | supported | test | library | | type | unknown | classification |
2+
| com/github/codeql/test/PublicClass.java:10:22:10:32 | staticStuff | com.github.codeql.test.PublicClass#staticStuff(String) | false | supported | test | library | | type | unknown | classification |
3+
| com/github/codeql/test/PublicClass.java:14:18:14:31 | nonPublicStuff | com.github.codeql.test.PublicClass#nonPublicStuff(String) | false | supported | test | library | | type | unknown | classification |
4+
| com/github/codeql/test/PublicInterface.java:4:17:4:21 | stuff | com.github.codeql.test.PublicInterface#stuff(String) | false | supported | test | library | | type | unknown | classification |
5+
| com/github/codeql/test/PublicInterface.java:6:24:6:34 | staticStuff | com.github.codeql.test.PublicInterface#staticStuff(String) | false | supported | test | library | | type | unknown | classification |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/FetchFrameworkModeMethods.ql
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.codeql.test;
2+
3+
class NonPublicClass {
4+
public void noCandidates(String here) {
5+
System.out.println(here);
6+
}
7+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.codeql.test;
2+
3+
import java.nio.file.Paths;
4+
5+
public class PublicClass {
6+
public void stuff(String arg) {
7+
System.out.println(arg);
8+
}
9+
10+
public static void staticStuff(String arg) {
11+
System.out.println(arg);
12+
}
13+
14+
protected void nonPublicStuff(String arg) {
15+
System.out.println(Paths.get("foo", arg));
16+
}
17+
18+
void packagePrivateStuff(String arg) {
19+
System.out.println(arg);
20+
}
21+
}

0 commit comments

Comments
 (0)