Added sources and steps for JMS API

This commit is contained in:
Artem Smotrakov
2022-04-17 12:18:42 +01:00
committed by Chris Smowton
parent 5c6aa15fe5
commit b6bd4f92d1
19 changed files with 306 additions and 0 deletions

View File

@@ -138,6 +138,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.MyBatis
private import semmle.code.java.frameworks.Hibernate
private import semmle.code.java.frameworks.jOOQ
private import semmle.code.java.frameworks.JMS
private import semmle.code.java.frameworks.RabbitMQ
}

View File

@@ -0,0 +1,112 @@
/*
* This model covers JMS API versions 1 and 2.
*
* https://docs.oracle.com/javaee/6/api/javax/jms/package-summary.html
* https://docs.oracle.com/javaee/7/api/javax/jms/package-summary.html
*/
import java
import semmle.code.java.dataflow.ExternalFlow
/** Defines sources of taited data in JMS 1. */
private class Jms1Source extends SourceModelCsv {
override predicate row(string row) {
row =
[
// incoming messages are considered tainted
"javax.jms;MessageListener;true;onMessage;(Message);;Parameter[0];remote",
"javax.jms;MessageConsumer;true;receive;;;ReturnValue;remote",
"javax.jms;MessageConsumer;true;receiveNoWait;();;ReturnValue;remote",
"javax.jms;QueueRequestor;true;request;(Message);;ReturnValue;remote",
"javax.jms;TopicRequestor;true;request;(Message);;ReturnValue;remote",
]
}
}
/** Defines taint propagation steps in JMS 1. */
private class Jms1FlowStep extends SummaryModelCsv {
override predicate row(string row) {
row =
[
// if a message is tainted, then it returns tainted data
"javax.jms;Message;true;getBody;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getJMSCorrelationIDAsBytes;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getJMSCorrelationID;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getJMSReplyTo;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getJMSDestination;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getJMSType;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getBooleanProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getByteProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getShortProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getIntProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getLongProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getFloatProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getDoubleProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getStringProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getObjectProperty;();;Argument[-1];ReturnValue;taint",
"javax.jms;Message;true;getPropertyNames;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readBoolean;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readByte;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readUnsignedByte;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readShort;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readUnsignedShort;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readChar;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readInt;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readLong;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readFloat;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readDouble;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readUTF;();;Argument[-1];ReturnValue;taint",
"javax.jms;BytesMessage;true;readBytes;;;Argument[-1];Argument[0];taint",
"javax.jms;MapMessage;true;getBoolean;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getByte;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getShort;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getChar;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getInt;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getLong;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getFloat;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getDouble;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getString;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getBytes;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getObject;(String);;Argument[-1];ReturnValue;taint",
"javax.jms;MapMessage;true;getMapNames;();;Argument[-1];ReturnValue;taint",
"javax.jms;ObjectMessage;true;getObject;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readBoolean;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readByte;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readShort;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readChar;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readInt;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readLong;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readFloat;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readDouble;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readString;();;Argument[-1];ReturnValue;taint",
"javax.jms;StreamMessage;true;readBytes;(byte[]);;Argument[-1];Argument[0];taint",
"javax.jms;StreamMessage;true;readObject;();;Argument[-1];ReturnValue;taint",
"javax.jms;TextMessage;true;getText;();;Argument[-1];ReturnValue;taint",
// if a destination is tainted, then it returns tainted data
"javax.jms;Queue;true;getQueueName;();;Argument[-1];ReturnValue;taint",
"javax.jms;Queue;true;toString;();;Argument[-1];ReturnValue;taint",
"javax.jms;Topic;true;getTopicName;();;Argument[-1];ReturnValue;taint",
"javax.jms;Topic;true;toString;();;Argument[-1];ReturnValue;taint",
]
}
}
/** Defines additional sources of taited data in JMS 2. */
private class Jms2Source extends SourceModelCsv {
override predicate row(string row) {
row =
[
"javax.jms;JMSConsumer;true;receive;;;ReturnValue;remote",
"javax.jms;JMSConsumer;true;receiveBody;;;ReturnValue;remote",
"javax.jms;JMSConsumer;true;receiveNoWait;();;ReturnValue;remote",
"javax.jms;JMSConsumer;true;receiveBodyNoWait;();;ReturnValue;remote",
]
}
}
/** Defines additional taint propagation steps in JMS 2. */
private class Jms2FlowStep extends SummaryModelCsv {
override predicate row(string row) {
row = ["javax.jms;Message;true;getBody;();;Argument[-1];ReturnValue;taint",]
}
}

View File

@@ -0,0 +1,30 @@
import java
import semmle.code.java.dataflow.FlowSources
import TestUtilities.InlineExpectationsTest
class TestConfig extends TaintTracking::Configuration {
TestConfig() { this = "TestConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess call |
call.getMethod().hasName("sink") and call.getArgument(0) = sink.asExpr()
)
}
}
class JmsFlowTest extends InlineExpectationsTest {
JmsFlowTest() { this = "JmsFlowTest" }
override string getARelevantTag() { result = "detected" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "detected" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink, TestConfig conf |
conf.hasFlowPath(source, sink)
|
location = sink.getNode().getLocation() and element = sink.getNode().toString() and value = ""
)
}
}

View File

@@ -0,0 +1,73 @@
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.jms.BytesMessage;
import javax.jms.MapMessage;
import javax.jms.ObjectMessage;
import javax.jms.StreamMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.QueueReceiver;
import javax.jms.QueueRequestor;
import javax.jms.TopicRequestor;
public class MessageListenerImpl implements MessageListener {
@Override
public void onMessage(Message message) { // $source
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText();
sink(text); // $detected
} else if (message instanceof BytesMessage) {
BytesMessage bytesMessage = (BytesMessage) message;
byte[] data = new byte[1024];
bytesMessage.readBytes(data, 42);
sink(new String(data)); // $detected
sink(bytesMessage.readUTF()); // $detected
} else if (message instanceof MapMessage) {
MapMessage mapMessage = (MapMessage) message;
sink(mapMessage.getString("data")); // $detected
sink(new String(mapMessage.getBytes("bytes"))); // $detected
} else if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
sink((String) objectMessage.getObject()); // $detected
} else if (message instanceof StreamMessage) {
StreamMessage streamMessage = (StreamMessage) message;
byte[] data = new byte[1024];
streamMessage.readBytes(data);
sink(new String(data)); // $detected
sink(streamMessage.readString()); // $detected
sink((String) streamMessage.readObject()); // $detected
}
} catch (Exception e) {
}
}
public void readFromCounsumer(MessageConsumer consumer) throws Exception {
TextMessage message = (TextMessage) consumer.receive(5000); // $source
String text = message.getText();
sink(text); // $detected
message = (TextMessage) consumer.receive(); // $source
text = message.getText();
sink(text); // $detected
message = (TextMessage) consumer.receiveNoWait(); // $source
text = message.getText();
sink(text); // $detected
}
public void readFromQueueRequestor(QueueRequestor requestor, Message message) throws Exception {
TextMessage reply = (TextMessage) requestor.request(message); // $source
String text = reply.getText();
sink(text); // $detected
}
public void readFromTopicRequestor(TopicRequestor requestor, Message message) throws Exception {
TextMessage reply = (TextMessage) requestor.request(message); // $source
String text = reply.getText();
sink(text); // $detected
}
private void sink(String data) {
}
}

View File

@@ -0,0 +1,16 @@
import java
import semmle.code.java.dataflow.FlowSources
import TestUtilities.InlineExpectationsTest
class JmsRemoteSourcesTest extends InlineExpectationsTest {
JmsRemoteSourcesTest() { this = "JmsRemoteSourcesTest" }
override string getARelevantTag() { result = "source" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "source" and
exists(RemoteFlowSource source |
location = source.getLocation() and element = source.toString() and value = ""
)
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jms-api-1

View File

@@ -0,0 +1,9 @@
package javax.jms;
public interface BytesMessage extends Message {
int readBytes(byte[] value);
int readBytes(byte[] value, int length);
String readUTF();
}

View File

@@ -0,0 +1,7 @@
package javax.jms;
public interface MapMessage extends Message {
byte[] getBytes(String name);
String getString(String name);
}

View File

@@ -0,0 +1,5 @@
package javax.jms;
public interface Message {
}

View File

@@ -0,0 +1,9 @@
package javax.jms;
public interface MessageConsumer {
Message receive();
Message receive(long timeout);
Message receiveNoWait();
}

View File

@@ -0,0 +1,5 @@
package javax.jms;
public interface MessageListener {
void onMessage(Message message);
}

View File

@@ -0,0 +1,5 @@
package javax.jms;
public interface ObjectMessage extends Message {
java.io.Serializable getObject();
}

View File

@@ -0,0 +1,5 @@
package javax.jms;
public interface QueueReceiver {
}

View File

@@ -0,0 +1,7 @@
package javax.jms;
public class QueueRequestor {
public Message request(Message message) {
return null;
}
}

View File

@@ -0,0 +1,9 @@
package javax.jms;
public interface StreamMessage extends Message {
int readBytes(byte[] value);
String readString();
Object readObject();
}

View File

@@ -0,0 +1,5 @@
package javax.jms;
public interface TextMessage extends Message {
String getText();
}

View File

@@ -0,0 +1,7 @@
package javax.jms;
public class TopicRequestor {
public Message request(Message message) {
return null;
}
}