Skip to content

Commit b5f3d77

Browse files
authored
Merge pull request #2759 from aschackmull/java/taint-tests
Java: Move some taint tests.
2 parents 163285b + 7d19eb7 commit b5f3d77

File tree

19 files changed

+632
-0
lines changed

19 files changed

+632
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import java.io.FileInputStream;
2+
import java.io.InputStream;
3+
import java.io.InputStreamReader;
4+
import java.io.Reader;
5+
import java.io.StringWriter;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import org.apache.commons.io.IOUtils;
9+
10+
class Test {
11+
public static void ioutils() {
12+
InputStream inp = new FileInputStream("test"); // user input
13+
14+
InputStream buf = IOUtils.buffer(inp);
15+
List<String> lines = IOUtils.readLines(inp, "UTF-8");
16+
byte[] bytes = IOUtils.readFully(inp, 1000);
17+
InputStream buf2 = IOUtils.toBufferedInputStream(inp);
18+
Reader bufread = IOUtils.toBufferedReader(new InputStreamReader(inp));
19+
byte[] bytes2 = IOUtils.toByteArray(inp, 1000);
20+
char[] chars = IOUtils.toCharArray(inp, "UTF-8");
21+
String s = IOUtils.toString(inp, "UTF-8");
22+
InputStream is = IOUtils.toInputStream(s, "UTF-8");
23+
24+
StringWriter writer = new StringWriter();
25+
writer.toString(); // not tainted
26+
IOUtils.copy(inp, writer, "UTF-8");
27+
writer.toString(); // tainted
28+
29+
writer = new StringWriter();
30+
writer.toString(); // not tainted
31+
IOUtils.copyLarge(bufread, writer);
32+
writer.toString(); // tainted
33+
34+
byte x;
35+
byte[] tgt = new byte[100];
36+
x = tgt[0]; // not tainted
37+
IOUtils.read(inp, tgt);
38+
x = tgt[0]; // tainted
39+
40+
tgt = new byte[100];
41+
x = tgt[0]; // not tainted
42+
IOUtils.readFully(inp, tgt);
43+
x = tgt[0]; // tainted
44+
45+
writer = new StringWriter();
46+
writer.toString(); // not tainted
47+
IOUtils.write(chars, writer);
48+
writer.toString(); // tainted
49+
50+
writer = new StringWriter();
51+
writer.toString(); // not tainted
52+
IOUtils.writeChunked(chars, writer);
53+
writer.toString(); // tainted
54+
55+
writer = new StringWriter();
56+
writer.toString(); // not tainted
57+
IOUtils.writeLines(lines, "\n", writer);
58+
writer.toString(); // tainted
59+
60+
writer = new StringWriter();
61+
writer.toString(); // not tainted
62+
IOUtils.writeLines(new ArrayList<String>(), s, writer);
63+
writer.toString(); // tainted
64+
}
65+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
| Test.java:12:21:12:47 | new FileInputStream(...) |
2+
| Test.java:14:21:14:39 | buffer(...) |
3+
| Test.java:14:36:14:38 | inp |
4+
| Test.java:15:24:15:54 | readLines(...) |
5+
| Test.java:15:42:15:44 | inp |
6+
| Test.java:16:18:16:45 | readFully(...) |
7+
| Test.java:16:36:16:38 | inp |
8+
| Test.java:17:22:17:55 | toBufferedInputStream(...) |
9+
| Test.java:17:52:17:54 | inp |
10+
| Test.java:18:20:18:71 | toBufferedReader(...) |
11+
| Test.java:18:45:18:70 | new InputStreamReader(...) |
12+
| Test.java:18:67:18:69 | inp |
13+
| Test.java:19:19:19:48 | toByteArray(...) |
14+
| Test.java:19:39:19:41 | inp |
15+
| Test.java:20:18:20:50 | toCharArray(...) |
16+
| Test.java:20:38:20:40 | inp |
17+
| Test.java:21:14:21:43 | toString(...) |
18+
| Test.java:21:31:21:33 | inp |
19+
| Test.java:22:20:22:52 | toInputStream(...) |
20+
| Test.java:22:42:22:42 | s |
21+
| Test.java:26:16:26:18 | inp |
22+
| Test.java:26:21:26:26 | writer |
23+
| Test.java:27:3:27:8 | writer |
24+
| Test.java:27:3:27:19 | toString(...) |
25+
| Test.java:31:21:31:27 | bufread |
26+
| Test.java:31:30:31:35 | writer |
27+
| Test.java:32:3:32:8 | writer |
28+
| Test.java:32:3:32:19 | toString(...) |
29+
| Test.java:37:16:37:18 | inp |
30+
| Test.java:37:21:37:23 | tgt |
31+
| Test.java:38:3:38:12 | ...=... |
32+
| Test.java:38:7:38:9 | tgt |
33+
| Test.java:38:7:38:12 | ...[...] |
34+
| Test.java:42:21:42:23 | inp |
35+
| Test.java:42:26:42:28 | tgt |
36+
| Test.java:43:3:43:12 | ...=... |
37+
| Test.java:43:7:43:9 | tgt |
38+
| Test.java:43:7:43:12 | ...[...] |
39+
| Test.java:47:17:47:21 | chars |
40+
| Test.java:47:24:47:29 | writer |
41+
| Test.java:48:3:48:8 | writer |
42+
| Test.java:48:3:48:19 | toString(...) |
43+
| Test.java:52:24:52:28 | chars |
44+
| Test.java:52:31:52:36 | writer |
45+
| Test.java:53:3:53:8 | writer |
46+
| Test.java:53:3:53:19 | toString(...) |
47+
| Test.java:57:22:57:26 | lines |
48+
| Test.java:57:35:57:40 | writer |
49+
| Test.java:58:3:58:8 | writer |
50+
| Test.java:58:3:58:19 | toString(...) |
51+
| Test.java:62:47:62:47 | s |
52+
| Test.java:62:50:62:55 | writer |
53+
| Test.java:63:3:63:8 | writer |
54+
| Test.java:63:3:63:19 | toString(...) |
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import semmle.code.java.dataflow.DataFlow
2+
import semmle.code.java.dataflow.TaintTracking
3+
import semmle.code.java.dataflow.FlowSources
4+
5+
class Conf extends TaintTracking::Configuration {
6+
Conf() { this = "qltest:dataflow:ioutils" }
7+
8+
override predicate isSource(DataFlow::Node source) { source instanceof UserInput }
9+
10+
override predicate isSink(DataFlow::Node sink) { any() }
11+
}
12+
13+
from UserInput u, DataFlow::Node e, Conf config
14+
where config.hasFlow(u, e) and e.getEnclosingCallable().hasName("ioutils")
15+
select e
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-io-2.6
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import java.io.ByteArrayInputStream;
2+
import java.io.ByteArrayOutputStream;
3+
import java.io.IOException;
4+
import java.io.ObjectInputStream;
5+
import java.io.ObjectOutput;
6+
import java.io.ObjectOutputStream;
7+
import java.util.StringTokenizer;
8+
9+
public class B {
10+
public static String[] taint() { return new String[] { "tainted" }; }
11+
12+
public static void sink(Object o) { }
13+
14+
public static void maintest() {
15+
String[] args = taint();
16+
// tainted - access to main args
17+
String[] aaaargs = args;
18+
sink(aaaargs);
19+
// tainted - access to tainted array
20+
String s = args[0];
21+
sink(s);
22+
// tainted - concatenation of tainted string
23+
String concat = "Look at me " + s + ", I'm tainted!";
24+
sink(concat);
25+
// tainted - parenthesised
26+
String pars = (concat);
27+
sink(pars);
28+
// tainted method argument, implies tainted return value
29+
String method = tainty(pars);
30+
sink(method);
31+
// tainted - complex
32+
String complex = ("Look at me " + args[0]) + ", I'm tainted!";
33+
sink(complex);
34+
// tainted - data preserving constructors
35+
String constructed = new String(complex);
36+
sink(constructed);
37+
// tainted - unsafe escape
38+
String badEscape = constructed.replaceAll("(<script>)", "");
39+
sink(badEscape);
40+
// tainted - tokenized string
41+
String token = new StringTokenizer(badEscape).nextToken();
42+
sink(token);
43+
44+
// not tainted
45+
String safe = notTainty(complex);
46+
sink(safe);
47+
String shouldBeFine = taintyOtherArg(safe, complex);
48+
sink(shouldBeFine);
49+
// non-whitelisted constructors don't pass taint
50+
StringWrapper herring = new StringWrapper(complex);
51+
sink(herring);
52+
53+
// tainted equality check with constant
54+
boolean cond = "foo" == s;
55+
sink(cond);
56+
// tainted logic with tainted operand
57+
boolean logic = cond && safe();
58+
sink(logic);
59+
// tainted condition
60+
sink(concat.endsWith("I'm tainted"));
61+
// tainted
62+
logic = safe() || cond;
63+
sink(logic);
64+
// tainted, use of equals
65+
logic = badEscape.equals("constant");
66+
sink(logic);
67+
68+
// not tainted
69+
boolean okay = s == shouldBeFine;
70+
sink(okay);
71+
72+
// methods on string that pass on taint
73+
String trimmed = s.trim();
74+
sink(trimmed);
75+
String[] split = s.split(" ");
76+
sink(split);
77+
String lower = s.toLowerCase();
78+
sink(lower);
79+
String upper = s.toUpperCase();
80+
sink(upper);
81+
byte[] bytes = s.getBytes("UTF-8");
82+
sink(bytes);
83+
String toString = s.toString();
84+
sink(toString);
85+
String subs = s.substring(1, 10);
86+
sink(subs);
87+
String repl = "some constant".replace(" ", s);
88+
sink(repl);
89+
String replAll = "some constant".replaceAll(" ", s);
90+
sink(replAll);
91+
String replFirst = "some constant".replaceFirst(" ", s);
92+
sink(replFirst);
93+
94+
ByteArrayOutputStream baos = null;
95+
ObjectOutput oos = null;
96+
ByteArrayInputStream bais = null;
97+
ObjectInputStream ois = null;
98+
try
99+
{
100+
// serialization of tainted string
101+
baos = new ByteArrayOutputStream();
102+
oos = new ObjectOutputStream(baos);
103+
oos.writeObject(s);
104+
byte[] serializedData = baos.toByteArray(); // tainted
105+
sink(serializedData);
106+
// serialization of fixed string
107+
baos = new ByteArrayOutputStream();
108+
oos = new ObjectOutputStream(baos);
109+
oos.writeObject("not tainted");
110+
byte[] serializedData2 = baos.toByteArray(); // *not* tainted
111+
sink(serializedData2);
112+
113+
// de-serialization of tainted string
114+
bais = new ByteArrayInputStream(serializedData);
115+
ois = new ObjectInputStream(bais);
116+
String deserializedData = (String)ois.readObject(); // tainted
117+
sink(deserializedData);
118+
} catch (IOException e) {
119+
// ignored in test code
120+
} catch (ClassNotFoundException e) {
121+
// ignored in test code
122+
}
123+
124+
// tainted array initializers
125+
String[] taintedArray = new String[] { s };
126+
sink(taintedArray);
127+
String[][] taintedArray2 = new String[][] { { s } };
128+
sink(taintedArray2);
129+
String[][][] taintedArray3 = new String[][][] { { { s } } };
130+
sink(taintedArray3);
131+
132+
return;
133+
}
134+
135+
public static String tainty(String arg) {
136+
// tainted return value
137+
return arg;
138+
}
139+
140+
public static String taintyOtherArg(String safe, String tainted) {
141+
return safe;
142+
}
143+
144+
public static String notTainty(String arg) {
145+
return "foo";
146+
}
147+
148+
public static class StringWrapper {
149+
public String wrapped;
150+
151+
public StringWrapper(String s) {
152+
this.wrapped = s;
153+
}
154+
}
155+
156+
public static boolean safe() {
157+
return true;
158+
}
159+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
public class MethodFlow {
2+
public static String taint() { return "tainted"; }
3+
4+
public static void sink(String s) { }
5+
6+
public void test() {
7+
String tainted = taint();
8+
sink(tainted);
9+
String tainted2 = notNull(taint());
10+
sink(tainted2);
11+
String tainted3 = wrapNotNull(taint());
12+
sink(tainted3);
13+
14+
String safe = notNull("a constant");
15+
sink(safe);
16+
17+
String diffString = returnDiffString(taint());
18+
sink(diffString);
19+
}
20+
21+
public <T> T notNull(T x) {
22+
if (x == null) {
23+
throw new NullPointerException();
24+
}
25+
return x;
26+
}
27+
28+
public <T> T wrapNotNull(T x) {
29+
T res = notNull(x);
30+
sink("Logged: " + res);
31+
return res;
32+
}
33+
34+
public String returnDiffString(String x) {
35+
sink("Received: " + x);
36+
return "OK";
37+
}
38+
}

0 commit comments

Comments
 (0)