Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ public static boolean isTaintInput(String value){
}


/**
* Check whether UTF-8 encoded bytes start with the prefix of a tainted value, ignoring case as
* {@link #isTaintInput(String)} does. Bytes that do not cannot decode into a tainted value, which
* this tells without decoding them.
*/
public static boolean startsWithTaintPrefix(byte[] utf8){
if(utf8 == null || utf8.length < PREFIX.length()){
return false;
}
for(int i=0; i<PREFIX.length(); i++){
/*
PREFIX is ASCII, and in UTF-8 an ASCII character is always the single byte with its value.
A byte of a multi-byte sequence is negative, so it never matches.
*/
if(Character.toLowerCase((char) utf8[i]) != Character.toLowerCase(PREFIX.charAt(i))){
return false;
}
}
return true;
}


public static boolean includesTaintInput(String value){
if(value == null){
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.util.Locale;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -16,6 +17,20 @@ public void testBase(){
assertTrue(TaintInputName.isTaintInput(name));
}

@Test
public void testStartsWithTaintPrefix(){
assertTrue(TaintInputName.startsWithTaintPrefix(TaintInputName.getTaintName(0).getBytes(StandardCharsets.UTF_8)));
assertTrue(TaintInputName.startsWithTaintPrefix("_em_1_xyz_".getBytes(StandardCharsets.UTF_8)));
// only the start is checked
assertTrue(TaintInputName.startsWithTaintPrefix("_EM_ and anything".getBytes(StandardCharsets.UTF_8)));

assertFalse(TaintInputName.startsWithTaintPrefix(null));
assertFalse(TaintInputName.startsWithTaintPrefix(new byte[0]));
assertFalse(TaintInputName.startsWithTaintPrefix("_EM".getBytes(StandardCharsets.UTF_8)));
assertFalse(TaintInputName.startsWithTaintPrefix("{\"a\": \"_EM_0_XYZ_\"}".getBytes(StandardCharsets.UTF_8)));
assertFalse(TaintInputName.startsWithTaintPrefix("ñ_EM_0_XYZ_".getBytes(StandardCharsets.UTF_8)));
}

@Test
public void testNegativeId(){
assertThrows(Exception.class, () -> TaintInputName.getTaintName(-1));
Expand Down
6 changes: 6 additions & 0 deletions client-java/instrumentation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@
<artifactId>jackson-jaxrs-base</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<!-- To check that the Jackson replacements apply inside its JsonDeserializer -->
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import org.evomaster.client.java.instrumentation.object.ClassToSchema;
import org.evomaster.client.java.instrumentation.object.JsonTaint;
import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

public class JsonUtils {

/**
* Size of the chunks a stream is read in
*/
private static final int READ_BUFFER_SIZE = 4096;

public static String readStream(InputStream src){
String content = new BufferedReader(
new InputStreamReader(src, Charset.defaultCharset()))
Expand All @@ -18,6 +25,42 @@ public static String readStream(InputStream src){
return content;
}

/**
* Consume the whole stream, keeping its bytes as they are (no decoding, no change of line separators).
* Java 8 has no InputStream.readAllBytes.
*/
public static byte[] readAllBytes(InputStream src) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[READ_BUFFER_SIZE];
int read;
while ((read = src.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
return out.toByteArray();
}

/**
* Decode raw JSON bytes as UTF-8, which is the encoding of JSON (RFC 8259), and the one tainted
* values are written in.
*/
public static String decodeUtf8(byte[] content){
return new String(content, StandardCharsets.UTF_8);
}

/**
* Same as {@link #decodeUtf8(byte[])}, but only when the bytes could decode into a tainted value
* (see {@link ExecutionTracer#couldBeTaintInput(byte[])}), so that large payloads are not decoded on
* every read for nothing.
*
* @return the decoded text, or null if it cannot be a tainted value
*/
public static String decodeUtf8IfCouldBeTaint(byte[] content){
if(!ExecutionTracer.couldBeTaintInput(content)){
return null;
}
return decodeUtf8(content);
}

public static InputStream toStream(String content){
return new ByteArrayInputStream(content.getBytes(Charset.defaultCharset()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public static List<MethodReplacementClass> getList() {
new InetSocketAddressClassReplacement(),
new KotlinIntrinsicsClassReplacement(),
new JacksonObjectMapperClassReplacement(),
new JacksonObjectReaderClassReplacement(),
new LocalDateClassReplacement(),
new LocalDateTimeClassReplacement(),
new LocalTimeClassReplacement(),
Expand Down
Loading
Loading