Merge branch 'master' into CWE-643

This commit is contained in:
SpaceWhite
2020-03-07 11:47:53 +09:00
943 changed files with 49930 additions and 23906 deletions

View File

@@ -0,0 +1,12 @@
/**
* Contains customizations to the standard library.
*
* This module is imported by `java.qll`, so any customizations defined here automatically
* apply to all queries.
*
* Typical examples of customizations include adding new subclasses of abstract classes such as
* the `RemoteFlowSource` and `AdditionalTaintStep` classes associated with the security queries
* to model frameworks that are not covered by the standard library.
*/
import java

View File

@@ -4,7 +4,7 @@ class Test {
{
String latlonCoords = args[1];
Runtime rt = Runtime.getRuntime();
Process exec = rt.exec("cmd.exe /C latlon2utm.exe -" + latlonCoords);
Process exec = rt.exec("cmd.exe /C latlon2utm.exe " + latlonCoords);
}
// GOOD: use an array of arguments instead of executing a string

View File

@@ -29,5 +29,12 @@ private class InsecureDefaultHttpResponseClassInstantiation extends InsecureNett
}
}
private class InsecureDefaultFullHttpResponseClassInstantiation extends InsecureNettyObjectCreation {
InsecureDefaultFullHttpResponseClassInstantiation() {
getConstructedType().hasQualifiedName("io.netty.handler.codec.http", "DefaultFullHttpResponse") and
getArgument(3).(CompileTimeConstantExpr).getBooleanValue() = false
}
}
from InsecureNettyObjectCreation new
select new, "Response-splitting vulnerability due to header value verification being disabled."

View File

@@ -0,0 +1 @@
This directory contains [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.

View File

@@ -1,5 +1,6 @@
/** Provides all default Java QL imports. */
import Customizations
import semmle.code.FileSystem
import semmle.code.Location
import semmle.code.java.Annotation

View File

@@ -167,6 +167,7 @@ class CompileTimeConstantExpr extends Expr {
/**
* Gets the string value of this expression, where possible.
*/
pragma[nomagic]
string getStringValue() {
result = this.(StringLiteral).getRepresentedString()
or

View File

@@ -364,13 +364,45 @@ private predicate typeFlow(TypeFlowNode n, RefType t) {
typeFlowJoin(lastRank(n), n, t)
}
pragma[nomagic]
predicate erasedTypeBound(RefType t) {
exists(RefType t0 | typeFlow(_, t0) and t = t0.getErasure())
}
pragma[nomagic]
predicate typeBound(RefType t) { typeFlow(_, t) }
/**
* Holds if we have a bound for `n` that is better than `t`, taking only erased
* types into account.
*/
pragma[nomagic]
private predicate irrelevantErasedBound(TypeFlowNode n, RefType t) {
exists(RefType bound |
typeFlow(n, bound)
or
n.getType() = bound and typeFlow(n, _)
|
t = bound.getErasure().(RefType).getASourceSupertype+() and
erasedTypeBound(t)
)
}
/**
* Holds if we have a bound for `n` that is better than `t`.
*/
pragma[noinline]
pragma[nomagic]
private predicate irrelevantBound(TypeFlowNode n, RefType t) {
exists(RefType bound | typeFlow(n, bound) or n.getType() = bound |
t = bound.getErasure().(RefType).getASourceSupertype+()
exists(RefType bound |
typeFlow(n, bound) and
t = bound.getASupertype+() and
typeBound(t) and
typeFlow(n, t) and
not t.getASupertype*() = bound
or
n.getType() = bound and
typeFlow(n, t) and
t = bound.getASupertype*()
)
}
@@ -380,7 +412,8 @@ private predicate irrelevantBound(TypeFlowNode n, RefType t) {
*/
private predicate bestTypeFlow(TypeFlowNode n, RefType t) {
typeFlow(n, t) and
not irrelevantBound(n, t.getErasure())
not irrelevantErasedBound(n, t.getErasure()) and
not irrelevantBound(n, t)
}
cached

View File

@@ -161,10 +161,27 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) {
/**
* Holds if the step from `n1` to `n2` is either extracting a value from a
* container, inserting a value into a container, or transforming one container
* to another.
* to another. This is restricted to cases where `n2` is the returned value of
* a call.
*/
predicate containerStep(Expr n1, Expr n2) {
qualifierToMethodStep(n1, n2) or
predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1, n2) }
/**
* Holds if the step from `n1` to `n2` is either extracting a value from a
* container, inserting a value into a container, or transforming one container
* to another. This is restricted to cases where the value of `n2` is being modified.
*/
predicate containerUpdateStep(Expr n1, Expr n2) {
qualifierToArgumentStep(n1, n2) or
argToQualifierStep(n1, n2)
}
/**
* Holds if the step from `n1` to `n2` is either extracting a value from a
* container, inserting a value into a container, or transforming one container
* to another.
*/
predicate containerStep(Expr n1, Expr n2) {
containerReturnValueStep(n1, n2) or
containerUpdateStep(n1, n2)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
private import java
private import DataFlowUtil
private import DataFlowImplCommon::Public
private import DataFlowImplCommon
private import DataFlowDispatch
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.SSA
@@ -120,7 +120,7 @@ predicate jumpStep(Node node1, Node node2) {
* Holds if `fa` is an access to an instance field that occurs as the
* destination of an assignment of the value `src`.
*/
predicate instanceFieldAssign(Expr src, FieldAccess fa) {
private predicate instanceFieldAssign(Expr src, FieldAccess fa) {
exists(AssignExpr a |
a.getSource() = src and
a.getDest() = fa and
@@ -324,3 +324,5 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
guard.controls(n.asExpr().getBasicBlock(), arg.getBooleanValue().booleanNot())
)
}
int accessPathLimit() { result = 5 }

View File

@@ -26,6 +26,8 @@ private newtype TNode =
e instanceof Argument and not e.getType() instanceof ImmutableType
or
exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier())
or
exists(ArrayAccess aa | e = aa.getArray())
} or
TImplicitExprPostUpdate(InstanceAccessExt ia) {
implicitInstanceArgument(_, ia)

View File

@@ -41,6 +41,9 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
or
localAdditionalTaintUpdateStep(src.asExpr(),
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
or
exists(Argument arg |
src.asExpr() = arg and
arg.isVararg() and
@@ -105,30 +108,20 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
or
sink.(LogicExpr).getAnOperand() = src
or
exists(Assignment assign | assign.getSource() = src |
sink = assign.getDest().(ArrayAccess).getArray()
)
or
exists(EnhancedForStmt for, SsaExplicitUpdate v |
for.getExpr() = src and
v.getDefiningExpr() = for.getVariable() and
v.getAFirstUse() = sink
)
or
containerStep(src, sink)
containerReturnValueStep(src, sink)
or
constructorStep(src, sink)
or
qualifierToMethodStep(src, sink)
or
qualifierToArgumentStep(src, sink)
or
argToMethodStep(src, sink)
or
argToArgStep(src, sink)
or
argToQualifierStep(src, sink)
or
comparisonStep(src, sink)
or
stringBuilderStep(src, sink)
@@ -136,6 +129,26 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
serializationStep(src, sink)
}
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
* different objects.
* This is restricted to cases where the step updates the value of `sink`.
*/
private predicate localAdditionalTaintUpdateStep(Expr src, Expr sink) {
exists(Assignment assign | assign.getSource() = src |
sink = assign.getDest().(ArrayAccess).getArray()
)
or
containerUpdateStep(src, sink)
or
qualifierToArgumentStep(src, sink)
or
argToArgStep(src, sink)
or
argToQualifierStep(src, sink)
}
private class BulkData extends RefType {
BulkData() {
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
@@ -242,7 +255,7 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
}
/** Access to a method that passes taint from qualifier to argument. */
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
private predicate qualifierToArgumentStep(Expr tracked, Expr sink) {
exists(MethodAccess ma, int arg |
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
tracked = ma.getQualifier() and
@@ -367,10 +380,25 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
taintPreservingArgumentToMethod(m, i) and
tracked = sink.(MethodAccess).getArgument(i)
)
or
exists(MethodAccess ma |
taintPreservingArgumentToMethod(ma.getMethod()) and
tracked = ma.getAnArgument() and
sink = ma
)
}
/**
* Holds if `method` is a library method that return tainted data if its
* Holds if `method` is a library method that returns tainted data if any
* of its arguments are tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method) {
method.getDeclaringType() instanceof TypeString and
(method.hasName("format") or method.hasName("join"))
}
/**
* Holds if `method` is a library method that returns tainted data if its
* `arg`th argument is tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
@@ -458,7 +486,7 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) {
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
* between arguments.
*/
private predicate argToArgStep(Expr tracked, RValue sink) {
private predicate argToArgStep(Expr tracked, Expr sink) {
exists(MethodAccess ma, Method method, int input, int output |
taintPreservingArgToArg(method, input, output) and
ma.getMethod() = method and

View File

@@ -305,6 +305,7 @@ private module Unification {
arg2 = t2.getTypeArgument(pos)
}
pragma[nomagic]
predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(

View File

@@ -0,0 +1 @@
This directory contains tests for [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.

View File

@@ -32,7 +32,6 @@ nodes
| A2.java:15:15:15:28 | new Integer(...) : Number | semmle.label | new Integer(...) : Number |
| A2.java:27:27:27:34 | o : Number | semmle.label | o : Number |
| A2.java:29:9:29:9 | o | semmle.label | o |
| A2.java:37:10:37:10 | o | semmle.label | o |
| A.java:14:29:14:36 | o : Number | semmle.label | o : Number |
| A.java:16:9:16:9 | o | semmle.label | o |
| A.java:20:30:20:37 | o : Number | semmle.label | o : Number |

View File

@@ -1,6 +1,7 @@
| Read | A.java:5:12:5:15 | this | A.java:5:12:5:19 | this.foo | A.java:2:7:2:9 | foo |
| Read | A.java:21:13:21:13 | a | A.java:21:13:21:22 | getFoo(...) | A.java:2:7:2:9 | foo |
| Read | A.java:23:9:23:9 | a | A.java:23:9:23:19 | aGetter(...) | A.java:2:7:2:9 | foo |
| Read | A.java:24:9:24:10 | a2 | A.java:24:9:24:23 | notAGetter(...) | A.java:2:7:2:9 | foo |
| Read | A.java:45:12:45:38 | maybeIdWrap(...) | A.java:45:12:45:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
| Read | A.java:49:12:49:38 | maybeIdWrap(...) | A.java:49:12:49:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
| Store | A.java:9:16:9:16 | x | A.java:9:5:9:8 | this [post update] | A.java:2:7:2:9 | foo |

View File

@@ -1,8 +1,18 @@
import java
import semmle.code.java.dataflow.internal.DataFlowImplCommon::Public
import semmle.code.java.dataflow.internal.DataFlowImplCommon
import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Public
import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Private
private predicate read(Node n1, Content f, Node n2) {
readDirect(n1, f, n2) or
argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
}
private predicate store(Node n1, Content f, Node n2) {
storeDirect(n1, f, n2) or
argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
}
from Node n1, Content f, Node n2, string k
where
read(n1, f, n2) and k = "Read"

View File

@@ -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
}
}

View File

@@ -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 [post update] |
| 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 [post update] |
| 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 [post update] |
| 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 [post update] |
| 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 [post update] |
| 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 [post update] |
| 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 [post update] |
| 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 [post update] |
| Test.java:63:3:63:8 | writer |
| Test.java:63:3:63:19 | toString(...) |

View File

@@ -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

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-io-2.6

View File

@@ -24,4 +24,52 @@ public class A {
bInput.read(b2);
sink(b2);
}
void streamWrite(ByteArrayOutputStream baos, byte[] data) {
baos.write(data);
}
void test3(ByteArrayOutputStream baos) {
streamWrite(baos, taint());
sink(baos.toByteArray());
}
static class BaosHolder {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
}
void streamWriteHolder(BaosHolder bh, byte[] data) {
bh.baos.write(data);
}
void test4(BaosHolder bh) {
streamWriteHolder(bh, taint());
sink(bh.baos.toByteArray());
}
static class DataHolder {
byte[] data = new byte[10];
}
void test5_a(DataHolder dh) {
ByteArrayInputStream bais = new ByteArrayInputStream(taint());
bais.read(dh.data);
test5_b(dh);
}
void test5_b(DataHolder dh) {
sink(dh.data);
}
void arrayWrite(byte[] from, byte[] to) {
for (int i = 0; i < 10; i++) {
to[i] = from[i];
}
}
void test6() {
byte[] b = new byte[10];
arrayWrite(taint(), b);
sink(b);
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -0,0 +1,66 @@
public class StringBuilderTests {
public static String taint() { return "tainted"; }
public static void sink(String s) { }
static void stringBuilderBad() {
StringBuilder sb = new StringBuilder();
sb.append("from preferences select locale where user='");
sb.append(taint());
sb.append("'");
sink(sb.toString());
}
static void stringBuilderOkay() {
StringBuilder sb = new StringBuilder();
sb.append("from preferences select locale where user='");
sb.append("fred");
sb.append("'");
sink(sb.toString());
}
static void stringBufferBad() {
StringBuffer sb = new StringBuffer();
sb.append("from preferences select locale where user='");
sb.append(taint());
sb.append("'");
sink(sb.toString());
}
static void stringBuilderNoVarBad() {
sink(new StringBuilder()
.append("from preferences select locale where user='")
.append(taint())
.append("'").toString()
);
}
static void stringBuilderConstructorBad() {
StringBuilder sb = new StringBuilder(taint());
sb.append("from preferences select locale where user='");
sb.append("fred");
sb.append("'");
sink(sb.toString());
}
static void stringBuilderMultipleAppendsBad() {
StringBuilder sb = new StringBuilder();
sb.append("from preferences select locale where user='").append(taint());
sb.append("'");
sink(sb.toString());
}
static void stringBuilderReplaceBad() {
StringBuilder sb = new StringBuilder();
sb.append("from preferences select locale where user='placeholder'");
sb.replace(45, 57, taint());
sink(sb.toString());
}
static void stringBuilderInsertBad() {
StringBuilder sb = new StringBuilder();
sb.append("from preferences select locale where user=''");
sb.insert(45, taint());
sink(sb.toString());
}
}

View File

@@ -0,0 +1,26 @@
public class Varargs {
public String taint() { return "tainted"; }
public void sink(String s) { }
public void sources() {
f1(taint());
f2(taint(), taint());
f3(new String[] { taint(), "" });
}
public void f1(String... ss) {
String s = ss[0];
sink(s);
}
public void f2(String... ss) {
String s = ss[0];
sink(s);
}
public void f3(String... ss) {
String s = ss[0];
sink(s);
}
}

View File

@@ -1,2 +1,51 @@
| A.java:10:19:10:25 | taint(...) | A.java:15:10:15:11 | b2 |
| A.java:20:19:20:25 | taint(...) | A.java:25:10:25:11 | b2 |
| A.java:33:23:33:29 | taint(...) | A.java:34:10:34:27 | toByteArray(...) |
| A.java:46:27:46:33 | taint(...) | A.java:47:10:47:30 | toByteArray(...) |
| A.java:55:58:55:64 | taint(...) | A.java:61:10:61:16 | dh.data |
| A.java:72:16:72:22 | taint(...) | A.java:73:10:73:10 | b |
| B.java:15:21:15:27 | taint(...) | B.java:18:10:18:16 | aaaargs |
| B.java:15:21:15:27 | taint(...) | B.java:21:10:21:10 | s |
| B.java:15:21:15:27 | taint(...) | B.java:24:10:24:15 | concat |
| B.java:15:21:15:27 | taint(...) | B.java:27:10:27:13 | pars |
| B.java:15:21:15:27 | taint(...) | B.java:30:10:30:15 | method |
| B.java:15:21:15:27 | taint(...) | B.java:33:10:33:16 | complex |
| B.java:15:21:15:27 | taint(...) | B.java:36:10:36:20 | constructed |
| B.java:15:21:15:27 | taint(...) | B.java:39:10:39:18 | badEscape |
| B.java:15:21:15:27 | taint(...) | B.java:42:10:42:14 | token |
| B.java:15:21:15:27 | taint(...) | B.java:55:10:55:13 | cond |
| B.java:15:21:15:27 | taint(...) | B.java:58:10:58:14 | logic |
| B.java:15:21:15:27 | taint(...) | B.java:60:10:60:39 | endsWith(...) |
| B.java:15:21:15:27 | taint(...) | B.java:63:10:63:14 | logic |
| B.java:15:21:15:27 | taint(...) | B.java:66:10:66:14 | logic |
| B.java:15:21:15:27 | taint(...) | B.java:74:10:74:16 | trimmed |
| B.java:15:21:15:27 | taint(...) | B.java:76:10:76:14 | split |
| B.java:15:21:15:27 | taint(...) | B.java:78:10:78:14 | lower |
| B.java:15:21:15:27 | taint(...) | B.java:80:10:80:14 | upper |
| B.java:15:21:15:27 | taint(...) | B.java:82:10:82:14 | bytes |
| B.java:15:21:15:27 | taint(...) | B.java:84:10:84:17 | toString |
| B.java:15:21:15:27 | taint(...) | B.java:86:10:86:13 | subs |
| B.java:15:21:15:27 | taint(...) | B.java:88:10:88:13 | repl |
| B.java:15:21:15:27 | taint(...) | B.java:90:10:90:16 | replAll |
| B.java:15:21:15:27 | taint(...) | B.java:92:10:92:18 | replFirst |
| B.java:15:21:15:27 | taint(...) | B.java:105:12:105:25 | serializedData |
| B.java:15:21:15:27 | taint(...) | B.java:117:12:117:27 | deserializedData |
| B.java:15:21:15:27 | taint(...) | B.java:126:10:126:21 | taintedArray |
| B.java:15:21:15:27 | taint(...) | B.java:128:10:128:22 | taintedArray2 |
| B.java:15:21:15:27 | taint(...) | B.java:130:10:130:22 | taintedArray3 |
| MethodFlow.java:7:22:7:28 | taint(...) | MethodFlow.java:8:10:8:16 | tainted |
| MethodFlow.java:9:31:9:37 | taint(...) | MethodFlow.java:10:10:10:17 | tainted2 |
| MethodFlow.java:11:35:11:41 | taint(...) | MethodFlow.java:12:10:12:17 | tainted3 |
| MethodFlow.java:11:35:11:41 | taint(...) | MethodFlow.java:30:10:30:25 | ... + ... |
| MethodFlow.java:17:42:17:48 | taint(...) | MethodFlow.java:35:10:35:25 | ... + ... |
| StringBuilderTests.java:9:15:9:21 | taint(...) | StringBuilderTests.java:11:10:11:22 | toString(...) |
| StringBuilderTests.java:25:15:25:21 | taint(...) | StringBuilderTests.java:27:10:27:22 | toString(...) |
| StringBuilderTests.java:33:15:33:21 | taint(...) | StringBuilderTests.java:31:10:34:29 | toString(...) |
| StringBuilderTests.java:39:42:39:48 | taint(...) | StringBuilderTests.java:43:10:43:22 | toString(...) |
| StringBuilderTests.java:48:69:48:75 | taint(...) | StringBuilderTests.java:50:10:50:22 | toString(...) |
| StringBuilderTests.java:56:24:56:30 | taint(...) | StringBuilderTests.java:57:10:57:22 | toString(...) |
| StringBuilderTests.java:63:19:63:25 | taint(...) | StringBuilderTests.java:64:10:64:22 | toString(...) |
| Varargs.java:7:8:7:14 | taint(...) | Varargs.java:14:10:14:10 | s |
| Varargs.java:8:8:8:14 | taint(...) | Varargs.java:19:10:19:10 | s |
| Varargs.java:8:17:8:23 | taint(...) | Varargs.java:19:10:19:10 | s |
| Varargs.java:9:23:9:29 | taint(...) | Varargs.java:24:10:24:10 | s |

View File

@@ -34,4 +34,38 @@ public class A {
Box b4 = Box.mk(taint());
sink(b4.getS1());
}
static class Box2 {
String s;
String getS() { return s; }
void setS(String s) { this.s = s; }
Box2(String s) {
setS(s + "1");
}
String getS1() { return getS() + "2"; }
String getS2() { return step(getS() + "_") + "2"; }
void setS1(String s) { setS("3" + s); }
void setS2(String s) { setS("3" + step("_" + s)); }
static Box2 mk(String s) {
Box2 b = new Box2("");
b.setS(step(s));
return b;
}
}
void foo2(Box2 b1, Box2 b2) {
b1.setS1(taint());
sink(b1.getS1());
b2.setS2(taint());
sink(b2.getS2());
String t3 = taint();
Box2 b3 = new Box2(step(t3));
sink(b3.s);
Box2 b4 = Box2.mk(taint());
sink(b4.getS1());
}
}

View File

@@ -2,3 +2,7 @@
| A.java:27:14:27:20 | taint(...) | A.java:28:10:28:19 | getS2(...) |
| A.java:30:17:30:23 | taint(...) | A.java:32:10:32:13 | b3.s |
| A.java:34:21:34:27 | taint(...) | A.java:35:10:35:19 | getS1(...) |
| A.java:58:14:58:20 | taint(...) | A.java:59:10:59:19 | getS1(...) |
| A.java:61:14:61:20 | taint(...) | A.java:62:10:62:19 | getS2(...) |
| A.java:64:17:64:23 | taint(...) | A.java:66:10:66:13 | b3.s |
| A.java:68:23:68:29 | taint(...) | A.java:69:10:69:19 | getS1(...) |

View File

@@ -0,0 +1,49 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class A {
public static void main(String[] args) {
String[] a = args; // user input
String s = args[0]; // user input
}
public static void userInput() throws SQLException, IOException, MalformedURLException {
System.getenv("test"); // user input
class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getParameter("test"); // remote user input
req.getHeader("test"); // remote user input
req.getQueryString(); // remote user input
req.getCookies()[0].getValue(); // remote user input
}
}
new Properties().getProperty("test"); // user input
System.getProperty("test"); // user input
new Object() {
public void test(ResultSet rs) throws SQLException {
rs.getString(0); // user input
}
};
new URL("test").openConnection().getInputStream(); // remote user input
new Socket("test", 1234).getInputStream(); // remote user input
InetAddress.getByName("test").getHostName(); // remote user input
System.in.read(); // user input
new FileInputStream("test").read(); // user input
}
}

View File

@@ -0,0 +1,8 @@
package security.library.dataflow;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RmiFlow extends Remote {
String listDirectory(String path);
}

View File

@@ -0,0 +1,15 @@
package security.library.dataflow;
public class RmiFlowImpl implements RmiFlow {
public String listDirectory(String path) {
String command = "ls " + path;
Runtime.getRuntime().exec(command);
return "pretend there are some results here";
}
public String notRemotable(String path) {
String command = "ls " + path;
Runtime.getRuntime().exec(command);
return "pretend there are some results here";
}
}

View File

@@ -0,0 +1,10 @@
| A.java:17:27:17:39 | args | A.java:17:27:17:39 | args |
| A.java:17:27:17:39 | args | A.java:18:18:18:21 | args |
| A.java:17:27:17:39 | args | A.java:19:16:19:19 | args |
| A.java:17:27:17:39 | args | A.java:19:16:19:22 | ...[...] |
| A.java:23:5:23:25 | getenv(...) | A.java:23:5:23:25 | getenv(...) |
| A.java:34:5:34:40 | getProperty(...) | A.java:34:5:34:40 | getProperty(...) |
| A.java:35:5:35:30 | getProperty(...) | A.java:35:5:35:30 | getProperty(...) |
| A.java:38:9:38:23 | getString(...) | A.java:38:9:38:23 | getString(...) |
| A.java:45:5:45:13 | System.in | A.java:45:5:45:13 | System.in |
| A.java:46:5:46:31 | new FileInputStream(...) | A.java:46:5:46:31 | new FileInputStream(...) |

View File

@@ -0,0 +1,18 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
class Conf extends TaintTracking::Configuration {
Conf() { this = "remote taint conf" }
override predicate isSource(DataFlow::Node n) {
n instanceof UserInput and
not n instanceof RemoteFlowSource
}
override predicate isSink(DataFlow::Node n) { any() }
}
from DataFlow::Node src, DataFlow::Node sink, Conf conf
where conf.hasFlow(src, sink)
select src, sink

View File

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

View File

@@ -0,0 +1,11 @@
| A.java:28:9:28:32 | getParameter(...) | A.java:28:9:28:32 | getParameter(...) |
| A.java:29:9:29:29 | getHeader(...) | A.java:29:9:29:29 | getHeader(...) |
| A.java:30:9:30:28 | getQueryString(...) | A.java:30:9:30:28 | getQueryString(...) |
| A.java:31:9:31:38 | getValue(...) | A.java:31:9:31:38 | getValue(...) |
| A.java:41:5:41:53 | getInputStream(...) | A.java:41:5:41:53 | getInputStream(...) |
| A.java:42:5:42:45 | getInputStream(...) | A.java:42:5:42:45 | getInputStream(...) |
| A.java:43:5:43:47 | getHostName(...) | A.java:43:5:43:47 | getHostName(...) |
| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:4:30:4:40 | path |
| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:20:5:31 | ... + ... |
| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:28:5:31 | path |
| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:6:29:6:35 | command |

View File

@@ -0,0 +1,15 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
class Conf extends TaintTracking::Configuration {
Conf() { this = "remote taint conf" }
override predicate isSource(DataFlow::Node n) { n instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node n) { any() }
}
from DataFlow::Node src, DataFlow::Node sink, Conf conf
where conf.hasFlow(src, sink)
select src, sink

View File

@@ -1,9 +1,7 @@
| A.java:9:10:9:11 | f1 | ArrayList<Long> | true |
| A.java:10:10:10:10 | l | List<Long> | false |
| A.java:11:19:11:20 | f2 | List<Long> | false |
| A.java:12:16:12:16 | x | List<Long> | false |
| A.java:14:9:14:9 | y | A | false |
| A.java:18:11:18:12 | l2 | List<Integer> | false |
| A.java:19:9:19:9 | y | List<Integer> | false |
| A.java:26:14:26:14 | o | List<Long> | false |
| A.java:34:11:34:11 | x | Integer | false |

View File

@@ -208,4 +208,16 @@ public class C {
}
}
}
public void ex15(Object o1, Object o2) {
if (o1 == null && o2 != null) {
return;
}
if (o1 == o2) {
return;
}
if (o1.equals(o2)) { // NPE - false positive
return;
}
}
}

View File

@@ -30,5 +30,6 @@
| C.java:144:15:144:15 | a | Variable $@ may be null here as suggested by $@ null guard. | C.java:141:20:141:26 | a | a | C.java:142:13:142:21 | ... == ... | this |
| C.java:188:9:188:11 | obj | Variable $@ may be null here because of $@ assignment. | C.java:181:5:181:22 | Object obj | obj | C.java:181:12:181:21 | obj | this |
| C.java:207:9:207:11 | obj | Variable $@ may be null here because of $@ assignment. | C.java:201:5:201:22 | Object obj | obj | C.java:201:12:201:21 | obj | this |
| C.java:219:9:219:10 | o1 | Variable $@ may be null here as suggested by $@ null guard. | C.java:212:20:212:28 | o1 | o1 | C.java:213:9:213:18 | ... == ... | this |
| F.java:11:5:11:7 | obj | Variable $@ may be null here as suggested by $@ null guard. | F.java:8:18:8:27 | obj | obj | F.java:9:9:9:19 | ... == ... | this |
| F.java:17:5:17:7 | obj | Variable $@ may be null here as suggested by $@ null guard. | F.java:14:18:14:27 | obj | obj | F.java:15:9:15:19 | ... == ... | this |

View File

@@ -0,0 +1,14 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,22 @@
package org.apache.commons.io;
import java.io.*;
import java.util.*;
public class IOUtils {
public static BufferedInputStream buffer(InputStream inputStream) { return null; }
public static void copy(InputStream input, Writer output, String inputEncoding) throws IOException { }
public static long copyLarge(Reader input, Writer output) throws IOException { return 42; }
public static int read(InputStream input, byte[] buffer) throws IOException { return 42; }
public static void readFully(InputStream input, byte[] buffer) throws IOException { }
public static byte[] readFully(InputStream input, int length) throws IOException { return null; }
public static List<String> readLines(InputStream input, String encoding) throws IOException { return null; }
public static InputStream toBufferedInputStream(InputStream input) throws IOException { return null; }
public static BufferedReader toBufferedReader(Reader reader) { return null; }
public static byte[] toByteArray(InputStream input, int size) throws IOException { return null; }
public static char[] toCharArray(InputStream is, String encoding) throws IOException { return null; }
public static InputStream toInputStream(String input, String encoding) throws IOException { return null; }
public static String toString(InputStream input, String encoding) throws IOException { return null; }
public static void write(char[] data, Writer output) throws IOException { }
public static void writeChunked(char[] data, Writer output) throws IOException { }
public static void writeLines(Collection<?> lines, String lineEnding, Writer writer) throws IOException { }
}