Skip to content

Commit 3c3a652

Browse files
author
Benjamin Muskalla
authored
Merge pull request #6664 from bmuskalla/bmuskalla/modelGenerator
Java: Initial CSV model generator
2 parents d232283 + b4eadef commit 3c3a652

30 files changed

+1381
-1
lines changed

java/ql/lib/semmle/code/java/frameworks/Strings.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ private class StringSummaryCsv extends SummaryModelCsv {
5454
"java.lang;StringBuffer;true;StringBuffer;(CharSequence);;Argument[0];Argument[-1];taint",
5555
"java.lang;StringBuffer;true;StringBuffer;(String);;Argument[0];Argument[-1];taint",
5656
"java.lang;StringBuilder;true;StringBuilder;;;Argument[0];Argument[-1];taint",
57-
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint"
57+
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint",
58+
"java.lang;CharSequence;true;toString;;;Argument[-1];ReturnValue;taint"
5859
]
5960
}
6061
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @name Capture sink models.
3+
* @description Finds public methods that act as sinks as they flow into a a known sink.
4+
* @id java/utils/model-generator/sink-models
5+
*/
6+
7+
import java
8+
private import Telemetry.ExternalAPI
9+
private import semmle.code.java.dataflow.DataFlow
10+
private import semmle.code.java.dataflow.TaintTracking
11+
private import semmle.code.java.dataflow.ExternalFlow
12+
private import ModelGeneratorUtils
13+
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
14+
15+
class PropagateToSinkConfiguration extends TaintTracking::Configuration {
16+
PropagateToSinkConfiguration() { this = "parameters or flowing into sinks" }
17+
18+
override predicate isSource(DataFlow::Node source) {
19+
(source.asExpr().(FieldAccess).isOwnFieldAccess() or source instanceof DataFlow::ParameterNode) and
20+
source.getEnclosingCallable().isPublic() and
21+
exists(RefType t |
22+
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
23+
not t instanceof TypeObject and
24+
t.isPublic()
25+
) and
26+
isRelevantForModels(source.getEnclosingCallable())
27+
}
28+
29+
override predicate isSink(DataFlow::Node sink) { sinkNode(sink, _) }
30+
31+
override DataFlow::FlowFeature getAFeature() {
32+
result instanceof DataFlow::FeatureHasSourceCallContext
33+
}
34+
}
35+
36+
string asInputArgument(DataFlow::Node source) {
37+
exists(int pos |
38+
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
39+
result = "Argument[" + pos + "]"
40+
)
41+
or
42+
source.asExpr() instanceof FieldAccess and
43+
result = "Argument[-1]"
44+
}
45+
46+
string captureSink(TargetAPI api) {
47+
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
48+
config.hasFlow(src, sink) and
49+
sinkNode(sink, kind) and
50+
api = src.getEnclosingCallable() and
51+
result = asSinkModel(api, asInputArgument(src), kind)
52+
)
53+
}
54+
55+
from TargetAPI api, string sink
56+
where sink = captureSink(api)
57+
select sink order by sink
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @name Capture source models.
3+
* @description Finds APIs that act as sources as they expose already known sources.
4+
* @id java/utils/model-generator/sink-models
5+
*/
6+
7+
import java
8+
private import Telemetry.ExternalAPI
9+
private import semmle.code.java.dataflow.DataFlow
10+
private import semmle.code.java.dataflow.TaintTracking
11+
private import semmle.code.java.dataflow.ExternalFlow
12+
private import ModelGeneratorUtils
13+
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
14+
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
15+
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
16+
private import semmle.code.java.dataflow.internal.DataFlowPrivate
17+
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
18+
19+
class FromSourceConfiguration extends TaintTracking::Configuration {
20+
FromSourceConfiguration() { this = "FromSourceConfiguration" }
21+
22+
override predicate isSource(DataFlow::Node source) { sourceNode(source, _) }
23+
24+
override predicate isSink(DataFlow::Node sink) {
25+
exists(TargetAPI c |
26+
sink instanceof ReturnNodeExt and
27+
sink.getEnclosingCallable() = c and
28+
c.isPublic() and
29+
c.fromSource()
30+
)
31+
}
32+
33+
override DataFlow::FlowFeature getAFeature() {
34+
result instanceof DataFlow::FeatureHasSinkCallContext
35+
}
36+
37+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
38+
isRelevantTaintStep(node1, node2)
39+
}
40+
}
41+
42+
string captureSource(TargetAPI api) {
43+
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
44+
config.hasFlow(source, sink) and
45+
sourceNode(source, kind) and
46+
api = source.getEnclosingCallable() and
47+
result = asSourceModel(api, returnNodeAsOutput(api, sink), kind)
48+
)
49+
}
50+
51+
from TargetAPI api, string sink
52+
where sink = captureSource(api)
53+
select sink order by sink
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* @name Capture summary models.
3+
* @description Finds applicable summary models to be used by other queries.
4+
* @id java/utils/model-generator/summary-models
5+
*/
6+
7+
import java
8+
import semmle.code.java.dataflow.TaintTracking
9+
import semmle.code.java.dataflow.internal.DataFlowImplCommon
10+
import semmle.code.java.dataflow.internal.DataFlowNodes
11+
import semmle.code.java.dataflow.internal.DataFlowPrivate
12+
import semmle.code.java.dataflow.InstanceAccess
13+
import ModelGeneratorUtils
14+
15+
string captureFlow(TargetAPI api) {
16+
result = captureQualifierFlow(api) or
17+
result = captureParameterFlowToReturnValue(api) or
18+
result = captureFieldFlowIn(api) or
19+
result = captureParameterToParameterFlow(api) or
20+
result = captureFieldFlow(api)
21+
}
22+
23+
/**
24+
* Capture fluent APIs that return `this`.
25+
* Example of a fluent API:
26+
* ```
27+
* public class Foo {
28+
* public Foo someAPI() {
29+
* // some side-effect
30+
* return this;
31+
* }
32+
* }
33+
* ```
34+
*/
35+
string captureQualifierFlow(TargetAPI api) {
36+
exists(ReturnStmt rtn |
37+
rtn.getEnclosingCallable() = api and
38+
rtn.getResult().(ThisAccess).isOwnInstanceAccess()
39+
) and
40+
result = asValueModel(api, "Argument[-1]", "ReturnValue")
41+
}
42+
43+
class FieldToReturnConfig extends TaintTracking::Configuration {
44+
FieldToReturnConfig() { this = "FieldToReturnConfig" }
45+
46+
override predicate isSource(DataFlow::Node source) {
47+
source instanceof DataFlow::InstanceParameterNode
48+
}
49+
50+
override predicate isSink(DataFlow::Node sink) {
51+
sink instanceof ReturnNodeExt and
52+
not sink.(ReturnNode).asExpr().(ThisAccess).isOwnInstanceAccess() and
53+
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable()))
54+
}
55+
56+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
57+
isRelevantTaintStep(node1, node2)
58+
}
59+
60+
override DataFlow::FlowFeature getAFeature() {
61+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
62+
}
63+
}
64+
65+
/**
66+
* Capture APIs that return tainted instance data.
67+
* Example of an API that returns tainted instance data:
68+
* ```
69+
* public class Foo {
70+
* private String tainted;
71+
*
72+
* public String returnsTainted() {
73+
* return tainted;
74+
* }
75+
*
76+
* public void putsTaintIntoParameter(List<String> foo) {
77+
* foo.add(tainted);
78+
* }
79+
* }
80+
* ```
81+
* Captured Model:
82+
* ```
83+
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
84+
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
85+
* ```
86+
*/
87+
string captureFieldFlow(TargetAPI api) {
88+
exists(FieldToReturnConfig config, ReturnNodeExt returnNodeExt |
89+
config.hasFlow(_, returnNodeExt) and
90+
returnNodeExt.getEnclosingCallable() = api and
91+
not api.getDeclaringType() instanceof EnumType and
92+
isRelevantType(returnNodeExt.getType())
93+
|
94+
result = asTaintModel(api, "Argument[-1]", returnNodeAsOutput(api, returnNodeExt))
95+
)
96+
}
97+
98+
class ParameterToFieldConfig extends TaintTracking::Configuration {
99+
ParameterToFieldConfig() { this = "ParameterToFieldConfig" }
100+
101+
override predicate isSource(DataFlow::Node source) {
102+
source instanceof DataFlow::ParameterNode and
103+
isRelevantType(source.getType())
104+
}
105+
106+
override predicate isSink(DataFlow::Node sink) {
107+
thisAccess(sink.(DataFlow::PostUpdateNode).getPreUpdateNode())
108+
}
109+
110+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
111+
store(node1, _, node2, _)
112+
}
113+
114+
override DataFlow::FlowFeature getAFeature() {
115+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
116+
}
117+
}
118+
119+
private predicate thisAccess(DataFlow::Node n) {
120+
n.asExpr().(InstanceAccess).isOwnInstanceAccess()
121+
or
122+
n.(DataFlow::ImplicitInstanceAccess).getInstanceAccess() instanceof OwnInstanceAccess
123+
}
124+
125+
/**
126+
* Captures APIs that accept input and store them in a field.
127+
* Example:
128+
* ```
129+
* public class Foo {
130+
* private String tainted;
131+
* public void doSomething(String input) {
132+
* tainted = input;
133+
* }
134+
* ```
135+
* Captured Model:
136+
* `p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint`
137+
*/
138+
string captureFieldFlowIn(TargetAPI api) {
139+
exists(DataFlow::Node source, ParameterToFieldConfig config |
140+
config.hasFlow(source, _) and
141+
source.asParameter().getCallable() = api
142+
|
143+
result =
144+
asTaintModel(api, "Argument[" + source.asParameter().getPosition() + "]", "Argument[-1]")
145+
)
146+
}
147+
148+
class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
149+
ParameterToReturnValueTaintConfig() { this = "ParameterToReturnValueTaintConfig" }
150+
151+
override predicate isSource(DataFlow::Node source) {
152+
exists(TargetAPI api |
153+
api = source.asParameter().getCallable() and
154+
isRelevantType(api.getReturnType()) and
155+
isRelevantType(source.asParameter().getType())
156+
)
157+
}
158+
159+
override predicate isSink(DataFlow::Node sink) { sink instanceof ReturnNode }
160+
161+
// consider store steps to track taint across objects to model factory methods returning tainted objects
162+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
163+
store(node1, _, node2, _)
164+
}
165+
166+
override DataFlow::FlowFeature getAFeature() {
167+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
168+
}
169+
}
170+
171+
predicate paramFlowToReturnValueExists(Parameter p) {
172+
exists(ParameterToReturnValueTaintConfig config, ReturnStmt rtn |
173+
config.hasFlow(DataFlow::parameterNode(p), DataFlow::exprNode(rtn.getResult()))
174+
)
175+
}
176+
177+
/**
178+
* Capture APIs that return (parts of) data passed in as a parameter.
179+
* Example:
180+
* ```
181+
* public class Foo {
182+
*
183+
* public String returnData(String tainted) {
184+
* return tainted.substring(0,10)
185+
* }
186+
* }
187+
* ```
188+
* Captured Model:
189+
* ```
190+
* p;Foo;true;returnData;;Argument[0];ReturnValue;taint
191+
* ```
192+
*/
193+
string captureParameterFlowToReturnValue(TargetAPI api) {
194+
exists(Parameter p |
195+
p = api.getAParameter() and
196+
paramFlowToReturnValueExists(p)
197+
|
198+
result = asTaintModel(api, parameterAccess(p), "ReturnValue")
199+
)
200+
}
201+
202+
/**
203+
* Capture APIs that pass tainted data from a parameter to a parameter.
204+
* Example:
205+
* ```
206+
* public class Foo {
207+
*
208+
* public void addToList(String tainted, List<String> foo) {
209+
* foo.add(tainted);
210+
* }
211+
* }
212+
* ```
213+
* Captured Model:
214+
* ```
215+
* p;Foo;true;addToList;;Argument[0];Argument[1];taint
216+
* ```
217+
*/
218+
string captureParameterToParameterFlow(TargetAPI api) {
219+
exists(DataFlow::ParameterNode source, DataFlow::PostUpdateNode sink |
220+
source.getEnclosingCallable() = api and
221+
sink.getPreUpdateNode().asExpr() = api.getAParameter().getAnAccess() and
222+
TaintTracking::localTaint(source, sink)
223+
|
224+
result =
225+
asTaintModel(api, parameterAccess(source.asParameter()),
226+
parameterAccess(sink.getPreUpdateNode().asExpr().(VarAccess).getVariable().(Parameter)))
227+
)
228+
}
229+
230+
from TargetAPI api, string flow
231+
where flow = captureFlow(api)
232+
select flow order by flow

0 commit comments

Comments
 (0)