Skip to content

Commit bb049bf

Browse files
authored
Merge pull request github#8765 from artem-smotrakov/cover-jms
Java: Add flow sources and steps for RabbitMQ and JMS
2 parents f95b585 + db90bf9 commit bb049bf

35 files changed

+515
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added flow sources and steps for JMS versions 1 and 2.
5+
* Added flow sources and steps for RabbitMQ.
6+
* Added flow steps for `java.io.DataInput` and `java.io.ObjectInput` implementations.

java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ private module Frameworks {
138138
private import semmle.code.java.frameworks.MyBatis
139139
private import semmle.code.java.frameworks.Hibernate
140140
private import semmle.code.java.frameworks.jOOQ
141+
private import semmle.code.java.frameworks.JMS
142+
private import semmle.code.java.frameworks.RabbitMQ
141143
}
142144

143145
private predicate sourceModelCsv(string row) {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* This model covers JMS API versions 1 and 2.
3+
*
4+
* https://docs.oracle.com/javaee/6/api/javax/jms/package-summary.html
5+
* https://docs.oracle.com/javaee/7/api/javax/jms/package-summary.html
6+
*/
7+
8+
import java
9+
private import semmle.code.java.dataflow.ExternalFlow
10+
11+
/** Defines sources of tainted data in JMS 1. */
12+
private class Jms1Source extends SourceModelCsv {
13+
override predicate row(string row) {
14+
row =
15+
[
16+
// incoming messages are considered tainted
17+
"javax.jms;MessageListener;true;onMessage;(Message);;Parameter[0];remote",
18+
"javax.jms;MessageConsumer;true;receive;;;ReturnValue;remote",
19+
"javax.jms;MessageConsumer;true;receiveNoWait;();;ReturnValue;remote",
20+
"javax.jms;QueueRequestor;true;request;(Message);;ReturnValue;remote",
21+
"javax.jms;TopicRequestor;true;request;(Message);;ReturnValue;remote",
22+
]
23+
}
24+
}
25+
26+
/** Defines taint propagation steps in JMS 1. */
27+
private class Jms1FlowStep extends SummaryModelCsv {
28+
override predicate row(string row) {
29+
row =
30+
[
31+
// if a message is tainted, then it returns tainted data
32+
"javax.jms;Message;true;getBody;();;Argument[-1];ReturnValue;taint",
33+
"javax.jms;Message;true;getJMSCorrelationIDAsBytes;();;Argument[-1];ReturnValue;taint",
34+
"javax.jms;Message;true;getJMSCorrelationID;();;Argument[-1];ReturnValue;taint",
35+
"javax.jms;Message;true;getJMSReplyTo;();;Argument[-1];ReturnValue;taint",
36+
"javax.jms;Message;true;getJMSDestination;();;Argument[-1];ReturnValue;taint",
37+
"javax.jms;Message;true;getJMSType;();;Argument[-1];ReturnValue;taint",
38+
"javax.jms;Message;true;getBooleanProperty;();;Argument[-1];ReturnValue;taint",
39+
"javax.jms;Message;true;getByteProperty;();;Argument[-1];ReturnValue;taint",
40+
"javax.jms;Message;true;getShortProperty;();;Argument[-1];ReturnValue;taint",
41+
"javax.jms;Message;true;getIntProperty;();;Argument[-1];ReturnValue;taint",
42+
"javax.jms;Message;true;getLongProperty;();;Argument[-1];ReturnValue;taint",
43+
"javax.jms;Message;true;getFloatProperty;();;Argument[-1];ReturnValue;taint",
44+
"javax.jms;Message;true;getDoubleProperty;();;Argument[-1];ReturnValue;taint",
45+
"javax.jms;Message;true;getStringProperty;();;Argument[-1];ReturnValue;taint",
46+
"javax.jms;Message;true;getObjectProperty;();;Argument[-1];ReturnValue;taint",
47+
"javax.jms;Message;true;getPropertyNames;();;Argument[-1];ReturnValue;taint",
48+
"javax.jms;BytesMessage;true;readBoolean;();;Argument[-1];ReturnValue;taint",
49+
"javax.jms;BytesMessage;true;readByte;();;Argument[-1];ReturnValue;taint",
50+
"javax.jms;BytesMessage;true;readUnsignedByte;();;Argument[-1];ReturnValue;taint",
51+
"javax.jms;BytesMessage;true;readShort;();;Argument[-1];ReturnValue;taint",
52+
"javax.jms;BytesMessage;true;readUnsignedShort;();;Argument[-1];ReturnValue;taint",
53+
"javax.jms;BytesMessage;true;readChar;();;Argument[-1];ReturnValue;taint",
54+
"javax.jms;BytesMessage;true;readInt;();;Argument[-1];ReturnValue;taint",
55+
"javax.jms;BytesMessage;true;readLong;();;Argument[-1];ReturnValue;taint",
56+
"javax.jms;BytesMessage;true;readFloat;();;Argument[-1];ReturnValue;taint",
57+
"javax.jms;BytesMessage;true;readDouble;();;Argument[-1];ReturnValue;taint",
58+
"javax.jms;BytesMessage;true;readUTF;();;Argument[-1];ReturnValue;taint",
59+
"javax.jms;BytesMessage;true;readBytes;;;Argument[-1];Argument[0];taint",
60+
"javax.jms;MapMessage;true;getBoolean;(String);;Argument[-1];ReturnValue;taint",
61+
"javax.jms;MapMessage;true;getByte;(String);;Argument[-1];ReturnValue;taint",
62+
"javax.jms;MapMessage;true;getShort;(String);;Argument[-1];ReturnValue;taint",
63+
"javax.jms;MapMessage;true;getChar;(String);;Argument[-1];ReturnValue;taint",
64+
"javax.jms;MapMessage;true;getInt;(String);;Argument[-1];ReturnValue;taint",
65+
"javax.jms;MapMessage;true;getLong;(String);;Argument[-1];ReturnValue;taint",
66+
"javax.jms;MapMessage;true;getFloat;(String);;Argument[-1];ReturnValue;taint",
67+
"javax.jms;MapMessage;true;getDouble;(String);;Argument[-1];ReturnValue;taint",
68+
"javax.jms;MapMessage;true;getString;(String);;Argument[-1];ReturnValue;taint",
69+
"javax.jms;MapMessage;true;getBytes;(String);;Argument[-1];ReturnValue;taint",
70+
"javax.jms;MapMessage;true;getObject;(String);;Argument[-1];ReturnValue;taint",
71+
"javax.jms;MapMessage;true;getMapNames;();;Argument[-1];ReturnValue;taint",
72+
"javax.jms;ObjectMessage;true;getObject;();;Argument[-1];ReturnValue;taint",
73+
"javax.jms;StreamMessage;true;readBoolean;();;Argument[-1];ReturnValue;taint",
74+
"javax.jms;StreamMessage;true;readByte;();;Argument[-1];ReturnValue;taint",
75+
"javax.jms;StreamMessage;true;readShort;();;Argument[-1];ReturnValue;taint",
76+
"javax.jms;StreamMessage;true;readChar;();;Argument[-1];ReturnValue;taint",
77+
"javax.jms;StreamMessage;true;readInt;();;Argument[-1];ReturnValue;taint",
78+
"javax.jms;StreamMessage;true;readLong;();;Argument[-1];ReturnValue;taint",
79+
"javax.jms;StreamMessage;true;readFloat;();;Argument[-1];ReturnValue;taint",
80+
"javax.jms;StreamMessage;true;readDouble;();;Argument[-1];ReturnValue;taint",
81+
"javax.jms;StreamMessage;true;readString;();;Argument[-1];ReturnValue;taint",
82+
"javax.jms;StreamMessage;true;readBytes;(byte[]);;Argument[-1];Argument[0];taint",
83+
"javax.jms;StreamMessage;true;readObject;();;Argument[-1];ReturnValue;taint",
84+
"javax.jms;TextMessage;true;getText;();;Argument[-1];ReturnValue;taint",
85+
// if a destination is tainted, then it returns tainted data
86+
"javax.jms;Queue;true;getQueueName;();;Argument[-1];ReturnValue;taint",
87+
"javax.jms;Queue;true;toString;();;Argument[-1];ReturnValue;taint",
88+
"javax.jms;Topic;true;getTopicName;();;Argument[-1];ReturnValue;taint",
89+
"javax.jms;Topic;true;toString;();;Argument[-1];ReturnValue;taint",
90+
]
91+
}
92+
}
93+
94+
/** Defines additional sources of tainted data in JMS 2. */
95+
private class Jms2Source extends SourceModelCsv {
96+
override predicate row(string row) {
97+
row =
98+
[
99+
"javax.jms;JMSConsumer;true;receive;;;ReturnValue;remote",
100+
"javax.jms;JMSConsumer;true;receiveBody;;;ReturnValue;remote",
101+
"javax.jms;JMSConsumer;true;receiveNoWait;();;ReturnValue;remote",
102+
"javax.jms;JMSConsumer;true;receiveBodyNoWait;();;ReturnValue;remote",
103+
]
104+
}
105+
}
106+
107+
/** Defines additional taint propagation steps in JMS 2. */
108+
private class Jms2FlowStep extends SummaryModelCsv {
109+
override predicate row(string row) {
110+
row = "javax.jms;Message;true;getBody;();;Argument[-1];ReturnValue;taint"
111+
}
112+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ private class JavaIoSummaryCsv extends SummaryModelCsv {
1313
"java.io;Writer;true;write;;;Argument[0];Argument[-1];taint",
1414
"java.io;Writer;true;toString;;;Argument[-1];ReturnValue;taint",
1515
"java.io;CharArrayWriter;true;toCharArray;;;Argument[-1];ReturnValue;taint",
16+
"java.io;ObjectInput;true;read;;;Argument[-1];Argument[0];taint",
17+
"java.io;DataInput;true;readFully;;;Argument[-1];Argument[0];taint",
18+
"java.io;DataInput;true;readLine;();;Argument[-1];ReturnValue;taint",
19+
"java.io;DataInput;true;readUTF;();;Argument[-1];ReturnValue;taint",
1620
"java.nio.channels;ReadableByteChannel;true;read;(ByteBuffer);;Argument[-1];Argument[0];taint",
1721
"java.nio.channels;Channels;false;newChannel;(InputStream);;Argument[0];ReturnValue;taint"
1822
]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Provides classes and predicates related to RabbitMQ.
3+
*/
4+
5+
import java
6+
private import semmle.code.java.dataflow.ExternalFlow
7+
8+
/**
9+
* Defines remote sources in RabbitMQ.
10+
*/
11+
private class RabbitMQSource extends SourceModelCsv {
12+
override predicate row(string row) {
13+
row =
14+
[
15+
// soruces for RabbitMQ 4.x
16+
"com.rabbitmq.client;Command;true;getContentHeader;();;ReturnValue;remote",
17+
"com.rabbitmq.client;Command;true;getContentBody;();;ReturnValue;remote",
18+
"com.rabbitmq.client;Consumer;true;handleDelivery;(String,Envelope,BasicProperties,byte[]);;Parameter[3];remote",
19+
"com.rabbitmq.client;QueueingConsumer;true;nextDelivery;;;ReturnValue;remote",
20+
"com.rabbitmq.client;RpcServer;true;handleCall;(Delivery,BasicProperties);;Parameter[0];remote",
21+
"com.rabbitmq.client;RpcServer;true;handleCall;(BasicProperties,byte[],BasicProperties);;Parameter[1];remote",
22+
"com.rabbitmq.client;RpcServer;true;handleCall;(byte[],BasicProperties);;Parameter[0];remote",
23+
"com.rabbitmq.client;RpcServer;true;preprocessReplyProperties;(Delivery,Builder);;Parameter[0];remote",
24+
"com.rabbitmq.client;RpcServer;true;postprocessReplyProperties;(Delivery,Builder);;Parameter[0];remote",
25+
"com.rabbitmq.client;RpcServer;true;handleCast;(Delivery);;Parameter[0];remote",
26+
"com.rabbitmq.client;RpcServer;true;handleCast;(BasicProperties,byte[]);;Parameter[1];remote",
27+
"com.rabbitmq.client;RpcServer;true;handleCast;(byte[]);;Parameter[0];remote",
28+
"com.rabbitmq.client;StringRpcServer;true;handleStringCall;;;Parameter[0];remote",
29+
"com.rabbitmq.client;RpcClient;true;doCall;;;ReturnValue;remote",
30+
"com.rabbitmq.client;RpcClient;true;primitiveCall;;;ReturnValue;remote",
31+
"com.rabbitmq.client;RpcClient;true;responseCall;;;ReturnValue;remote",
32+
"com.rabbitmq.client;RpcClient;true;stringCall;(String);;ReturnValue;remote",
33+
"com.rabbitmq.client;RpcClient;true;mapCall;;;ReturnValue;remote",
34+
"com.rabbitmq.client.impl;Frame;true;getInputStream;();;ReturnValue;remote",
35+
"com.rabbitmq.client.impl;Frame;true;getPayload;();;ReturnValue;remote",
36+
"com.rabbitmq.client.impl;FrameHandler;true;readFrame;();;ReturnValue;remote",
37+
]
38+
}
39+
}
40+
41+
/**
42+
* Defines flow steps in RabbitMQ.
43+
*/
44+
private class RabbitMQSummaryCsv extends SummaryModelCsv {
45+
override predicate row(string row) {
46+
row =
47+
[
48+
// flow steps for RabbitMQ 4.x
49+
"com.rabbitmq.client;GetResponse;true;GetResponse;;;Argument[2];Argument[-1];taint",
50+
"com.rabbitmq.client;GetResponse;true;getBody;();;Argument[-1];ReturnValue;taint",
51+
"com.rabbitmq.client;RpcClient$Response;true;getBody;();;Argument[-1];ReturnValue;taint",
52+
"com.rabbitmq.client;QueueingConsumer$Delivery;true;getBody;();;Argument[-1];ReturnValue;taint",
53+
"com.rabbitmq.client.impl;Frame;false;fromBodyFragment;(int,byte[],int,int);;Argument[1];ReturnValue;taint",
54+
"com.rabbitmq.client.impl;Frame;false;readFrom;(DataInputStream);;Argument[0];ReturnValue;taint",
55+
"com.rabbitmq.client.impl;Frame;true;writeTo;(DataOutputStream);;Argument[-1];Argument[0];taint",
56+
]
57+
}
58+
}

java/ql/test/library-tests/frameworks/guava/handwritten/TestIO.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void test3() throws IOException {
9494
sink(ByteStreams.newDataInput(btaint())); // $numTaintFlow=1
9595
sink(ByteStreams.newDataInput(btaint(), 0)); // $numTaintFlow=1
9696
sink(ByteStreams.newDataInput(btaint())); // $numTaintFlow=1
97-
sink(ByteStreams.newDataInput(btaint()).readLine()); // $ MISSING:numTaintFlow=1
97+
sink(ByteStreams.newDataInput(btaint()).readLine()); // $numTaintFlow=1
9898
sink(ByteStreams.newDataInput(new ByteArrayInputStream(btaint()))); // $numTaintFlow=1
9999
ByteArrayOutputStream out = new ByteArrayOutputStream();
100100
out.write(btaint());
@@ -136,6 +136,6 @@ void test6() throws IOException {
136136
new CountingInputStream(itaint()).read(buf, 0, 42);
137137
sink(buf); // $numTaintFlow=1
138138
sink(new LittleEndianDataInputStream(itaint())); // $numTaintFlow=1
139-
sink(new LittleEndianDataInputStream(itaint()).readUTF()); // $ MISSING:numTaintFlow=1
139+
sink(new LittleEndianDataInputStream(itaint()).readUTF()); // $numTaintFlow=1
140140
}
141141
}

java/ql/test/library-tests/frameworks/jms/FlowTest.expected

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import java
2+
import semmle.code.java.dataflow.FlowSources
3+
import TestUtilities.InlineExpectationsTest
4+
5+
class TestConfig extends TaintTracking::Configuration {
6+
TestConfig() { this = "TestConfig" }
7+
8+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
9+
10+
override predicate isSink(DataFlow::Node sink) {
11+
exists(MethodAccess call |
12+
call.getMethod().hasName("sink") and call.getArgument(0) = sink.asExpr()
13+
)
14+
}
15+
}
16+
17+
class JmsFlowTest extends InlineExpectationsTest {
18+
JmsFlowTest() { this = "JmsFlowTest" }
19+
20+
override string getARelevantTag() { result = "tainted" }
21+
22+
override predicate hasActualResult(Location location, string element, string tag, string value) {
23+
tag = "tainted" and
24+
exists(DataFlow::PathNode source, DataFlow::PathNode sink, TestConfig conf |
25+
conf.hasFlowPath(source, sink)
26+
|
27+
location = sink.getNode().getLocation() and element = sink.getNode().toString() and value = ""
28+
)
29+
}
30+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import javax.jms.MessageListener;
2+
import javax.jms.TextMessage;
3+
import javax.jms.BytesMessage;
4+
import javax.jms.MapMessage;
5+
import javax.jms.ObjectMessage;
6+
import javax.jms.StreamMessage;
7+
import javax.jms.Message;
8+
import javax.jms.MessageConsumer;
9+
import javax.jms.QueueReceiver;
10+
import javax.jms.QueueRequestor;
11+
import javax.jms.TopicRequestor;
12+
13+
public class MessageListenerImpl implements MessageListener {
14+
15+
@Override
16+
public void onMessage(Message message) { // $source
17+
try {
18+
if (message instanceof TextMessage) {
19+
TextMessage textMessage = (TextMessage) message;
20+
String text = textMessage.getText();
21+
sink(text); // $tainted
22+
} else if (message instanceof BytesMessage) {
23+
BytesMessage bytesMessage = (BytesMessage) message;
24+
byte[] data = new byte[1024];
25+
bytesMessage.readBytes(data, 42);
26+
sink(new String(data)); // $tainted
27+
sink(bytesMessage.readUTF()); // $tainted
28+
} else if (message instanceof MapMessage) {
29+
MapMessage mapMessage = (MapMessage) message;
30+
sink(mapMessage.getString("data")); // $tainted
31+
sink(new String(mapMessage.getBytes("bytes"))); // $tainted
32+
} else if (message instanceof ObjectMessage) {
33+
ObjectMessage objectMessage = (ObjectMessage) message;
34+
sink((String) objectMessage.getObject()); // $tainted
35+
} else if (message instanceof StreamMessage) {
36+
StreamMessage streamMessage = (StreamMessage) message;
37+
byte[] data = new byte[1024];
38+
streamMessage.readBytes(data);
39+
sink(new String(data)); // $tainted
40+
sink(streamMessage.readString()); // $tainted
41+
sink((String) streamMessage.readObject()); // $tainted
42+
}
43+
} catch (Exception e) {
44+
}
45+
}
46+
47+
public void readFromCounsumer(MessageConsumer consumer) throws Exception {
48+
TextMessage message = (TextMessage) consumer.receive(5000); // $source
49+
String text = message.getText();
50+
sink(text); // $tainted
51+
message = (TextMessage) consumer.receive(); // $source
52+
text = message.getText();
53+
sink(text); // $tainted
54+
message = (TextMessage) consumer.receiveNoWait(); // $source
55+
text = message.getText();
56+
sink(text); // $tainted
57+
}
58+
59+
public void readFromQueueRequestor(QueueRequestor requestor, Message message) throws Exception {
60+
TextMessage reply = (TextMessage) requestor.request(message); // $source
61+
String text = reply.getText();
62+
sink(text); // $tainted
63+
}
64+
65+
public void readFromTopicRequestor(TopicRequestor requestor, Message message) throws Exception {
66+
TextMessage reply = (TextMessage) requestor.request(message); // $source
67+
String text = reply.getText();
68+
sink(text); // $tainted
69+
}
70+
71+
private void sink(String data) {
72+
}
73+
}

java/ql/test/library-tests/frameworks/jms/RemoteSourcesTest.expected

Whitespace-only changes.

0 commit comments

Comments
 (0)