Skip to content

Commit 739fe75

Browse files
author
Benjamin Muskalla
committed
Support flow for factory and strategy pattern
* Support models for factories that create new instances of an object while tainting it with incoming data * Support models to infer super types for private implementations to expose the models at the right level
1 parent 58de6d1 commit 739fe75

7 files changed

Lines changed: 78 additions & 24 deletions

File tree

java/ql/src/utils/model-generator/CaptureSinkModels.ql

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@
55
*/
66

77
import java
8-
import Telemetry.ExternalAPI
9-
import semmle.code.java.dataflow.DataFlow
10-
import semmle.code.java.dataflow.TaintTracking
11-
import semmle.code.java.dataflow.ExternalFlow
12-
import ModelGeneratorUtils
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
1314

1415
class PropagateToSinkConfiguration extends TaintTracking::Configuration {
15-
PropagateToSinkConfiguration() { this = "parameters on public api flowing into sinks" }
16+
PropagateToSinkConfiguration() { this = "parameters or flowing into sinks" }
1617

1718
override predicate isSource(DataFlow::Node source) {
18-
source instanceof DataFlow::ParameterNode and
19+
(source.asExpr() instanceof FieldAccess or source instanceof DataFlow::ParameterNode) and
1920
source.getEnclosingCallable().isPublic() and
20-
source.getEnclosingCallable().getDeclaringType().isPublic() and
21+
exists(RefType t |
22+
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
23+
not t instanceof TypeObject and
24+
t.isPublic()
25+
) and
2126
isRelevantForModels(source.getEnclosingCallable())
2227
}
2328

@@ -29,6 +34,9 @@ string asInputArgument(DataFlow::Node source) {
2934
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
3035
result = "Argument[" + pos + "]"
3136
)
37+
or
38+
source.asExpr() instanceof FieldAccess and
39+
result = "Argument[-1]"
3240
}
3341

3442
string captureSink(Callable api) {
@@ -42,4 +50,6 @@ string captureSink(Callable api) {
4250

4351
from TargetAPI api, string sink
4452
where sink = captureSink(api)
53+
// and
54+
// api.hasName("openStream")
4555
select sink order by sink

java/ql/src/utils/model-generator/CaptureSourceModels.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ private import ModelGeneratorUtils
1313
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
1414
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
1515
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
16-
import semmle.code.java.dataflow.internal.DataFlowNodes::Private
16+
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
1717

1818
class FromSourceConfiguration extends TaintTracking::Configuration {
1919
FromSourceConfiguration() { this = "FromSourceConfiguration" }

java/ql/src/utils/model-generator/CaptureSummaryModels.ql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ string captureFieldFlowIn(Callable api) {
119119
exists(DataFlow::ParameterNode source, DataFlow::ExprNode sink, ParameterToFieldConfig config |
120120
sink.asExpr().getEnclosingCallable().getDeclaringType() =
121121
source.asParameter().getCallable().getDeclaringType() and
122+
not api.isStatic() and
122123
config.hasFlow(source, sink) and
123124
source.asParameter().getCallable() = api
124125
|
@@ -140,6 +141,11 @@ class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
140141
}
141142

142143
override predicate isSink(DataFlow::Node sink) { sink instanceof ReturnNodeExt }
144+
145+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
146+
node2.asExpr().(ConstructorCall).getAnArgument() = node1.asExpr() and
147+
node1.asExpr().(Argument).getCall().getCallee().fromSource()
148+
}
143149
}
144150

145151
predicate paramFlowToReturnValueExists(Parameter p) {

java/ql/src/utils/model-generator/ModelGeneratorUtils.qll

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@ import java
22
import semmle.code.java.dataflow.ExternalFlow
33
import semmle.code.java.dataflow.internal.ContainerFlow
44

5+
Method superImpl(Method m) {
6+
result = m.getAnOverride() and
7+
not exists(result.getAnOverride()) and
8+
not m instanceof ToStringMethod
9+
}
10+
511
class TargetAPI extends Callable {
612
TargetAPI() {
713
this.isPublic() and
814
this.fromSource() and
9-
this.getDeclaringType().isPublic() and
15+
(
16+
this.getDeclaringType().isPublic() or
17+
superImpl(this).getDeclaringType().isPublic()
18+
) and
1019
isRelevantForModels(this)
1120
}
1221
}
@@ -70,7 +79,7 @@ string asSourceModel(Callable api, string output, string kind) {
7079
private string asPartialModel(Callable api) {
7180
result =
7281
typeAsSummaryModel(api) + ";" //
73-
+ isExtensible(api.getDeclaringType()).toString() + ";" //
82+
+ isExtensible(bestTypeForModel(api)) + ";" //
7483
+ api.getName() + ";" //
7584
+ paramsString(api) + ";" //
7685
+ /* ext + */ ";" //
@@ -80,18 +89,12 @@ private string asPartialModel(Callable api) {
8089
* Returns the appropriate type name for the model. Either the type
8190
* declaring the method or the supertype introducing the method.
8291
*/
83-
private string typeAsSummaryModel(Callable api) {
84-
if exists(superImpl(api.(Method)))
85-
then
86-
superImpl(api.(Method)).fromSource() and
87-
result = typeAsModel(superImpl(api.(Method)).getDeclaringType())
88-
else result = typeAsModel(api.getDeclaringType())
89-
}
92+
private string typeAsSummaryModel(Callable api) { result = typeAsModel(bestTypeForModel(api)) }
9093

91-
Method superImpl(Method m) {
92-
result = m.getAnOverride() and
93-
not exists(result.getAnOverride()) and
94-
not m instanceof ToStringMethod
94+
private RefType bestTypeForModel(Callable api) {
95+
if exists(superImpl(api))
96+
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
97+
else result = api.getDeclaringType()
9598
}
9699

97100
private string typeAsModel(RefType type) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
| p;PrivateFlowViaPublicInterface$SPI;true;openStream;();;Argument[-1];create-file; |
12
| p;Sinks;true;copyFileToDirectory;(Path,Path,CopyOption[]);;Argument[1];create-file; |
23
| p;Sinks;true;readUrl;(URL,Charset);;Argument[0];open-url; |
34
| p;Sources;true;readUrl;(URL);;Argument[0];open-url; |

java/ql/test/utils/model-generator/CaptureSummaryModels.expected

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
| p;Factory;false;create;(String);;Argument[0];Argument[-1];taint; |
2-
| p;Factory;false;create;(String,int);;Argument[0];Argument[-1];taint; |
1+
| p;Factory;false;create;(String);;Argument[0];ReturnValue;taint; |
2+
| p;Factory;false;create;(String,int);;Argument[0];ReturnValue;taint; |
33
| p;Factory;false;getValue;();;Argument[-1];ReturnValue;taint; |
44
| p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;taint; |
55
| p;FluentAPI;false;returnsThis;(String);;Argument[-1];ReturnValue;value; |
@@ -38,3 +38,4 @@
3838
| p;Pojo;false;getCharArray;();;Argument[-1];ReturnValue;taint; |
3939
| p;Pojo;false;getValue;();;Argument[-1];ReturnValue;taint; |
4040
| p;Pojo;false;setValue;(String);;Argument[0];Argument[-1];taint; |
41+
| p;PrivateFlowViaPublicInterface;true;createAnSPI;(File);;Argument[0];ReturnValue;taint; |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package p;
2+
3+
import java.io.File;
4+
import java.io.FileOutputStream;
5+
import java.io.IOException;
6+
import java.io.OutputStream;
7+
8+
public class PrivateFlowViaPublicInterface {
9+
10+
public static interface SPI {
11+
OutputStream openStream() throws IOException;
12+
}
13+
14+
private static final class PrivateImplWithSink implements SPI {
15+
16+
private File file;
17+
18+
public PrivateImplWithSink(File file) {
19+
this.file = file;
20+
}
21+
22+
@Override
23+
public OutputStream openStream() throws IOException {
24+
return new FileOutputStream(file);
25+
}
26+
27+
}
28+
29+
public static SPI createAnSPI(File file) {
30+
return new PrivateImplWithSink(file);
31+
}
32+
33+
}

0 commit comments

Comments
 (0)