Java: Add modelling for Guava Strings, Splitter, and Joiner

This commit is contained in:
Joe
2020-09-30 16:00:43 +01:00
committed by Joe Farebrother
parent a92a701c35
commit e196c75b4e
6 changed files with 264 additions and 0 deletions

View File

@@ -79,6 +79,11 @@ Expr clearlyNotNullExpr(Expr reason) {
)
or
exists(SsaVariable v | clearlyNotNull(v, reason) and result = v.getAUse())
or
exists(Method m | m = result.(MethodAccess).getMethod() and reason = result |
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
m.hasName("nullToEmpty")
)
}
/** Holds if `v` is an SSA variable that is provably not `null`. */
@@ -146,6 +151,11 @@ predicate nullCheckMethod(Method m, boolean branch, boolean isnull) {
m.hasName("isNotEmpty") and
branch = true and
isnull = false
or
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
m.hasName("isNullOrEmpty") and
branch = false and
isnull = false
}
/**

View File

@@ -7,9 +7,14 @@ private import semmle.code.java.security.SecurityTests
private import semmle.code.java.security.Validation
private import semmle.code.java.Maps
private import semmle.code.java.dataflow.internal.ContainerFlow
<<<<<<< HEAD
private import semmle.code.java.frameworks.spring.SpringController
private import semmle.code.java.frameworks.spring.SpringHttp
import semmle.code.java.dataflow.FlowSteps
=======
private import semmle.code.java.frameworks.jackson.JacksonSerializability
private import semmle.code.java.frameworks.guava.Guava
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
/**
* Holds if taint can flow from `src` to `sink` in zero or more
@@ -285,7 +290,11 @@ private predicate taintPreservingQualifierToArgument(Method m, int arg) {
m.hasName("read") and
arg = 0
or
<<<<<<< HEAD
m.(TaintPreservingCallable).transfersTaint(-1, arg)
=======
m.(GuavaTaintPropagationMethod).propagatesTaint(-1, arg)
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
}
/** Access to a method that passes taint from the qualifier. */
@@ -359,7 +368,11 @@ private predicate taintPreservingQualifierToMethod(Method m) {
)
)
or
<<<<<<< HEAD
m.(TaintPreservingCallable).returnsTaintFrom(-1)
=======
m.(GuavaTaintPropagationMethod).propagatesTaint(-1, -2)
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
}
private class StringReplaceMethod extends TaintPreservingCallable {
@@ -477,7 +490,26 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) {
method.hasName("sourceToInputSource") and
arg = 0
or
<<<<<<< HEAD
method.(TaintPreservingCallable).returnsTaintFrom(arg)
=======
exists(ProtobufParser p | method = p.getAParseFromMethod()) and
arg = 0
or
exists(ProtobufMessageLite m | method = m.getAParseFromMethod()) and
arg = 0
or
// Jackson serialization methods that return the serialized data
method instanceof JacksonWriteValueMethod and
method.getNumberOfParameters() = 1 and
arg = 0
or
method.getDeclaringType().hasQualifiedName("java.io", "StringWriter") and
method.hasName("append") and
arg = 0
or
method.(GuavaTaintPropagationMethod).propagatesTaint(arg, -2)
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
}
/**
@@ -525,7 +557,17 @@ private predicate taintPreservingArgToArg(Method method, int input, int output)
input = 0 and
output = 2
or
<<<<<<< HEAD
method.(TaintPreservingCallable).transfersTaint(input, output)
=======
// Jackson serialization methods that write data to the first argument
method instanceof JacksonWriteValueMethod and
method.getNumberOfParameters() > 1 and
input = method.getNumberOfParameters() - 1 and
output = 0
or
method.(GuavaTaintPropagationMethod).propagatesTaint(input, output)
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
}
/**
@@ -553,7 +595,18 @@ private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
write.getDeclaringType().hasQualifiedName("java.io", "OutputStream")
)
or
<<<<<<< HEAD
method.(TaintPreservingCallable).transfersTaint(arg, -1)
=======
exists(Method append |
method.overrides*(append) and
append.hasName("append") and
arg = 0 and
append.getDeclaringType().hasQualifiedName("java.io", "StringWriter")
)
or
method.(GuavaTaintPropagationMethod).propagatesTaint(arg, -1)
>>>>>>> 61c00e344... Java: Add modelling for Guava `Strings`, `Splitter`, and `Joiner`
}
/** A comparison or equality test with a constant. */

View File

@@ -0,0 +1,30 @@
import java
import Strings
import Splitter
import Joiner
/**
* A method in the guava framework that propegates taint.
*/
abstract class GuavaTaintPropagationMethod extends Method {
/**
* Holds if this method propagates taint between the given source and sink.
* `src` and `sink` are indicies of arguments to this method, or -1 to represent the qualifier.
* `sink` ca also be -2 to represent the return value.
*/
abstract predicate propagatesTaint(int src, int sink);
}
/**
* A method in the guava framework that returns tainted data when a specific input
* (either an argument or the qualifier) is tainted.
*/
abstract class GuavaTaintPropagationMethodToReturn extends GuavaTaintPropagationMethod {
/**
* Holds if this method returns tainted data when the given source is tainted.
* `src` is an argument index, or -1 to indicate the qualifier.
*/
abstract predicate propagatesTaint(int src);
override predicate propagatesTaint(int src, int sink) { propagatesTaint(src) and sink = -2 }
}

View File

@@ -0,0 +1,93 @@
import java
import Guava
/**
* The class `com.google.common.base.Joiner`.
*/
class TypeGuavaJoiner extends Class {
TypeGuavaJoiner() { this.hasQualifiedName("com.google.common.base", "Joiner") }
}
/**
* The nested class `Joiner.MapJoiner`.
*/
class TypeGuavaMapJoiner extends NestedClass {
TypeGuavaMapJoiner() {
this.getEnclosingType() instanceof TypeGuavaJoiner and
this.hasName("MapJoiner")
}
}
/**
* A method of `Joiner` or `MapJoiner`.
*/
private class GuavaJoinerMethod extends Method {
GuavaJoinerMethod() {
this.getDeclaringType() instanceof TypeGuavaJoiner or
this.getDeclaringType() instanceof TypeGuavaMapJoiner
}
}
/**
* A method that builds a `Joiner` or `MapJoiner`.
*/
class GuavaJoinerBuilderMethod extends GuavaJoinerMethod, GuavaTaintPropagationMethodToReturn {
GuavaJoinerBuilderMethod() {
// static Joiner on(char separator)
// static Joiner on(String separator)
// Joiner skipNulls()
// Joiner useForNull(String nullText)
// Joiner.MapJoiner withKeyValueSeparator(String keyValueSeparator)
// Joiner.MapJoiner useForNull(String nullText) [on MapJoiner]
this.hasName(["on", "skipNulls", "useForNull", "withKeyValueSeparator"])
}
override predicate propagatesTaint(int src) { src = [-1, 0] }
}
/**
* An `appendTo` method on `Joiner` or `MapJoiner`
*/
class GuavaJoinerAppendToMethod extends GuavaJoinerMethod, GuavaTaintPropagationMethod {
GuavaJoinerAppendToMethod() {
// <A extends Appendable> A appendTo(A appendable, Iterable<?> parts)
// <A extends Appendable> A appendTo(A appendable, Iterator<?> parts)
// <A extends Appendable> A appendTo(A appendable, Object[] parts)
// <A extends Appendable> A appendTo(A appendable, Object first, Object second, Object... rest)
// StringBuilder appendTo(StringBuilder builder, Iterable<?> parts)
// StringBuilder appendTo(StringBuilder builder, Iterator<?> parts)
// StringBuilder appendTo(StringBuilder builder, Object[] parts)
// StringBuilder appendTo(StringBuilder builder, Object first, Object second, Object... rest)
// <A extends Appendable> A appendTo(A appendable, Iterable<? extends Map.Entry<?,?>> entries) [on MapJoiner]
// <A extends Appendable> A appendTo(A appendable, Iterator<? extends Map.Entry<?,?>> parts)
// <A extends Appendable> A appendTo(A appendable, Map<?,?> map)
// StringBuilder appendTo(StringBuilder builder, Iterable<? extends Map.Entry<?,?>> entries)
// StringBuilder appendTo(StringBuilder builder, Iterator<? extends Map.Entry<?,?>> entries)
// StringBuilder appendTo(StringBuilder builder, Map<?,?> map)
this.hasName("appendTo")
}
override predicate propagatesTaint(int src, int sink) {
src = [-1 .. getNumberOfParameters()] and
src != sink and
sink = [-2, 0]
}
}
/**
* A `join` method on `Joiner` or `MapJoiner`
*/
class GuavaJoinMethod extends GuavaJoinerMethod, GuavaTaintPropagationMethodToReturn {
GuavaJoinMethod() {
// String join(Iterable<?> parts)
// String join(Iterator<?> parts)
// String join(Object[] parts)
// String join(Object first, Object second, Object... rest)
// String join(Iterable<? extends Map.Entry<?,?>> entries) [on MapJoiner]
// String join(Iterator<? extends Map.Entry<?,?>> entries)
// String join(Map<?,?> map)
this.hasName("join")
}
override predicate propagatesTaint(int src) { src = [-1 .. getNumberOfParameters()] }
}

View File

@@ -0,0 +1,39 @@
import java
import Guava
/**
* The class `com.google.common.base.Splitter`.
*/
class TypeGuavaSplitter extends Class {
TypeGuavaSplitter() { this.hasQualifiedName("com.google.common.base", "Splitter") }
}
/**
* The nested class `Splitter.MapSplitter`.
*/
class TypeGuavaMapSplitter extends NestedClass {
TypeGuavaMapSplitter() {
this.getEnclosingType() instanceof TypeGuavaSplitter and
this.hasName("MapSplitter")
}
}
/**
* A method of `Splitter` or `MapSplitter` that splits its input string.
*/
class GuavaSplitMethod extends GuavaTaintPropagationMethodToReturn {
GuavaSplitMethod() {
(
this.getDeclaringType() instanceof TypeGuavaSplitter
or
this.getDeclaringType() instanceof TypeGuavaMapSplitter
) and
// Iterable<String> split(CharSequence sequence)
// List<String> splitToList(CharSequence sequence)
// Stream<String> splitToStream(CharSequence sequence)
// Map<String,String> split(CharSequence sequence) [on MapSplitter]
this.hasName(["split", "splitToList", "splitToStream"])
}
override predicate propagatesTaint(int src) { src = 0 }
}

View File

@@ -0,0 +1,39 @@
import java
import Guava
/**
* The class `com.google.common.base.Strings`.
*/
class TypeGuavaStrings extends Class {
TypeGuavaStrings() { this.hasQualifiedName("com.google.common.base", "Strings") }
}
/**
* A Guava string utility method that preserves taint from its first argument.
*/
class GuavaStringsTaintPropagationMethod extends GuavaTaintPropagationMethodToReturn {
GuavaStringsTaintPropagationMethod() {
this.getDeclaringType() instanceof TypeGuavaStrings and
// static String emptyToNull(String string)
// static String emptyToNull(String string)
// static String padEnd(String string, int minLength, char padChar)
// static String padStart(String string, int minLength, char padChar)
// static String repeat(String string, int count)
this.hasName(["emptyToNull", "nullToEmpty", "padStart", "padEnd", "repeat"])
}
override predicate propagatesTaint(int src) { src = 0 }
}
/**
* The method `Strings.lenientFormat`.
*/
class GuavaStringsFormatMethod extends GuavaTaintPropagationMethodToReturn {
GuavaStringsFormatMethod() {
this.getDeclaringType() instanceof TypeGuavaStrings and
// static String lenientFormat(String template, Object ... args)
this.hasName("lenientFormat")
}
override predicate propagatesTaint(int src) { src in [0 .. getNumberOfParameters()] }
}