Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions java/ql/test/library-tests/dataflow/taint-ioutils/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;

class Test {
public static void ioutils() {
InputStream inp = new FileInputStream("test"); // user input

InputStream buf = IOUtils.buffer(inp);
List<String> lines = IOUtils.readLines(inp, "UTF-8");
byte[] bytes = IOUtils.readFully(inp, 1000);
InputStream buf2 = IOUtils.toBufferedInputStream(inp);
Reader bufread = IOUtils.toBufferedReader(new InputStreamReader(inp));
byte[] bytes2 = IOUtils.toByteArray(inp, 1000);
char[] chars = IOUtils.toCharArray(inp, "UTF-8");
String s = IOUtils.toString(inp, "UTF-8");
InputStream is = IOUtils.toInputStream(s, "UTF-8");

StringWriter writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.copy(inp, writer, "UTF-8");
writer.toString(); // tainted

writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.copyLarge(bufread, writer);
writer.toString(); // tainted

byte x;
byte[] tgt = new byte[100];
x = tgt[0]; // not tainted
IOUtils.read(inp, tgt);
x = tgt[0]; // tainted

tgt = new byte[100];
x = tgt[0]; // not tainted
IOUtils.readFully(inp, tgt);
x = tgt[0]; // tainted

writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.write(chars, writer);
writer.toString(); // tainted

writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.writeChunked(chars, writer);
writer.toString(); // tainted

writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.writeLines(lines, "\n", writer);
writer.toString(); // tainted

writer = new StringWriter();
writer.toString(); // not tainted
IOUtils.writeLines(new ArrayList<String>(), s, writer);
writer.toString(); // tainted
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
| Test.java:12:21:12:47 | new FileInputStream(...) |
| Test.java:14:21:14:39 | buffer(...) |
| Test.java:14:36:14:38 | inp |
| Test.java:15:24:15:54 | readLines(...) |
| Test.java:15:42:15:44 | inp |
| Test.java:16:18:16:45 | readFully(...) |
| Test.java:16:36:16:38 | inp |
| Test.java:17:22:17:55 | toBufferedInputStream(...) |
| Test.java:17:52:17:54 | inp |
| Test.java:18:20:18:71 | toBufferedReader(...) |
| Test.java:18:45:18:70 | new InputStreamReader(...) |
| Test.java:18:67:18:69 | inp |
| Test.java:19:19:19:48 | toByteArray(...) |
| Test.java:19:39:19:41 | inp |
| Test.java:20:18:20:50 | toCharArray(...) |
| Test.java:20:38:20:40 | inp |
| Test.java:21:14:21:43 | toString(...) |
| Test.java:21:31:21:33 | inp |
| Test.java:22:20:22:52 | toInputStream(...) |
| Test.java:22:42:22:42 | s |
| Test.java:26:16:26:18 | inp |
| Test.java:26:21:26:26 | writer |
| Test.java:27:3:27:8 | writer |
| Test.java:27:3:27:19 | toString(...) |
| Test.java:31:21:31:27 | bufread |
| Test.java:31:30:31:35 | writer |
| Test.java:32:3:32:8 | writer |
| Test.java:32:3:32:19 | toString(...) |
| Test.java:37:16:37:18 | inp |
| Test.java:37:21:37:23 | tgt |
| Test.java:38:3:38:12 | ...=... |
| Test.java:38:7:38:9 | tgt |
| Test.java:38:7:38:12 | ...[...] |
| Test.java:42:21:42:23 | inp |
| Test.java:42:26:42:28 | tgt |
| Test.java:43:3:43:12 | ...=... |
| Test.java:43:7:43:9 | tgt |
| Test.java:43:7:43:12 | ...[...] |
| Test.java:47:17:47:21 | chars |
| Test.java:47:24:47:29 | writer |
| Test.java:48:3:48:8 | writer |
| Test.java:48:3:48:19 | toString(...) |
| Test.java:52:24:52:28 | chars |
| Test.java:52:31:52:36 | writer |
| Test.java:53:3:53:8 | writer |
| Test.java:53:3:53:19 | toString(...) |
| Test.java:57:22:57:26 | lines |
| Test.java:57:35:57:40 | writer |
| Test.java:58:3:58:8 | writer |
| Test.java:58:3:58:19 | toString(...) |
| Test.java:62:47:62:47 | s |
| Test.java:62:50:62:55 | writer |
| Test.java:63:3:63:8 | writer |
| Test.java:63:3:63:19 | toString(...) |
15 changes: 15 additions & 0 deletions java/ql/test/library-tests/dataflow/taint-ioutils/dataFlow.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources

class Conf extends TaintTracking::Configuration {
Conf() { this = "qltest:dataflow:ioutils" }

override predicate isSource(DataFlow::Node source) { source instanceof UserInput }

override predicate isSink(DataFlow::Node sink) { any() }
}

from UserInput u, DataFlow::Node e, Conf config
where config.hasFlow(u, e) and e.getEnclosingCallable().hasName("ioutils")
select e
1 change: 1 addition & 0 deletions java/ql/test/library-tests/dataflow/taint-ioutils/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-io-2.6
159 changes: 159 additions & 0 deletions java/ql/test/library-tests/dataflow/taint/B.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.StringTokenizer;

public class B {
public static String[] taint() { return new String[] { "tainted" }; }

public static void sink(Object o) { }

public static void maintest() {
String[] args = taint();
// tainted - access to main args
String[] aaaargs = args;
sink(aaaargs);
// tainted - access to tainted array
String s = args[0];
sink(s);
// tainted - concatenation of tainted string
String concat = "Look at me " + s + ", I'm tainted!";
sink(concat);
// tainted - parenthesised
String pars = (concat);
sink(pars);
// tainted method argument, implies tainted return value
String method = tainty(pars);
sink(method);
// tainted - complex
String complex = ("Look at me " + args[0]) + ", I'm tainted!";
sink(complex);
// tainted - data preserving constructors
String constructed = new String(complex);
sink(constructed);
// tainted - unsafe escape
String badEscape = constructed.replaceAll("(<script>)", "");
sink(badEscape);
// tainted - tokenized string
String token = new StringTokenizer(badEscape).nextToken();
sink(token);

// not tainted
String safe = notTainty(complex);
sink(safe);
String shouldBeFine = taintyOtherArg(safe, complex);
sink(shouldBeFine);
// non-whitelisted constructors don't pass taint
StringWrapper herring = new StringWrapper(complex);
sink(herring);

// tainted equality check with constant
boolean cond = "foo" == s;
sink(cond);
// tainted logic with tainted operand
boolean logic = cond && safe();
sink(logic);
// tainted condition
sink(concat.endsWith("I'm tainted"));
// tainted
logic = safe() || cond;
sink(logic);
// tainted, use of equals
logic = badEscape.equals("constant");
sink(logic);

// not tainted
boolean okay = s == shouldBeFine;
sink(okay);

// methods on string that pass on taint
String trimmed = s.trim();
sink(trimmed);
String[] split = s.split(" ");
sink(split);
String lower = s.toLowerCase();
sink(lower);
String upper = s.toUpperCase();
sink(upper);
byte[] bytes = s.getBytes("UTF-8");
sink(bytes);
String toString = s.toString();
sink(toString);
String subs = s.substring(1, 10);
sink(subs);
String repl = "some constant".replace(" ", s);
sink(repl);
String replAll = "some constant".replaceAll(" ", s);
sink(replAll);
String replFirst = "some constant".replaceFirst(" ", s);
sink(replFirst);

ByteArrayOutputStream baos = null;
ObjectOutput oos = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try
{
// serialization of tainted string
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(s);
byte[] serializedData = baos.toByteArray(); // tainted
sink(serializedData);
// serialization of fixed string
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject("not tainted");
byte[] serializedData2 = baos.toByteArray(); // *not* tainted
sink(serializedData2);

// de-serialization of tainted string
bais = new ByteArrayInputStream(serializedData);
ois = new ObjectInputStream(bais);
String deserializedData = (String)ois.readObject(); // tainted
sink(deserializedData);
} catch (IOException e) {
// ignored in test code
} catch (ClassNotFoundException e) {
// ignored in test code
}

// tainted array initializers
String[] taintedArray = new String[] { s };
sink(taintedArray);
String[][] taintedArray2 = new String[][] { { s } };
sink(taintedArray2);
String[][][] taintedArray3 = new String[][][] { { { s } } };
sink(taintedArray3);

return;
}

public static String tainty(String arg) {
// tainted return value
return arg;
}

public static String taintyOtherArg(String safe, String tainted) {
return safe;
}

public static String notTainty(String arg) {
return "foo";
}

public static class StringWrapper {
public String wrapped;

public StringWrapper(String s) {
this.wrapped = s;
}
}

public static boolean safe() {
return true;
}
}
38 changes: 38 additions & 0 deletions java/ql/test/library-tests/dataflow/taint/MethodFlow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
public class MethodFlow {
public static String taint() { return "tainted"; }

public static void sink(String s) { }

public void test() {
String tainted = taint();
sink(tainted);
String tainted2 = notNull(taint());
sink(tainted2);
String tainted3 = wrapNotNull(taint());
sink(tainted3);

String safe = notNull("a constant");
sink(safe);

String diffString = returnDiffString(taint());
sink(diffString);
}

public <T> T notNull(T x) {
if (x == null) {
throw new NullPointerException();
}
return x;
}

public <T> T wrapNotNull(T x) {
T res = notNull(x);
sink("Logged: " + res);
return res;
}

public String returnDiffString(String x) {
sink("Received: " + x);
return "OK";
}
}
Loading