diff options
Diffstat (limited to 'examples/junit/src/test/java/com/example')
19 files changed, 1060 insertions, 0 deletions
diff --git a/examples/junit/src/test/java/com/example/AutofuzzFuzzTest.java b/examples/junit/src/test/java/com/example/AutofuzzFuzzTest.java new file mode 100644 index 00000000..4f316c34 --- /dev/null +++ b/examples/junit/src/test/java/com/example/AutofuzzFuzzTest.java @@ -0,0 +1,41 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.code_intelligence.jazzer.junit.FuzzTest; + +class AutofuzzFuzzTest { + private static class IntHolder { + private final int i; + + IntHolder(int i) { + this.i = i; + } + + public int getI() { + return i; + } + } + + @FuzzTest(maxDuration = "5m") + void autofuzz(String str, IntHolder holder) { + assumeTrue(holder != null); + if (holder.getI() == 1234 && str != null && str.contains("jazzer")) { + throw new RuntimeException(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/AutofuzzLifecycleFuzzTest.java b/examples/junit/src/test/java/com/example/AutofuzzLifecycleFuzzTest.java new file mode 100644 index 00000000..b82f1aba --- /dev/null +++ b/examples/junit/src/test/java/com/example/AutofuzzLifecycleFuzzTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; + +@TestMethodOrder(MethodOrderer.MethodName.class) +@ExtendWith(AutofuzzLifecycleFuzzTest.AutofuzzLifecycleInstancePostProcessor.class) +class AutofuzzLifecycleFuzzTest { + // Use a TestInstancePostProcessor to inject an object into the JUnit test instance, + // simulating other JUnit extensions like the Spring Boot Test, to check that autofuzz + // invokes the test function on the correct instance. + private Object injectedObject; + + @FuzzTest(maxDuration = "1s") + void autofuzzLifecycleFuzz(String ignored, String ignoredAsWell) { + Assertions.assertNotNull(injectedObject); + } + + static class AutofuzzLifecycleInstancePostProcessor implements TestInstancePostProcessor { + @Override + public void postProcessTestInstance(Object o, ExtensionContext extensionContext) { + ((AutofuzzLifecycleFuzzTest) o).injectedObject = new Object(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/AutofuzzWithCorpusFuzzTest.java b/examples/junit/src/test/java/com/example/AutofuzzWithCorpusFuzzTest.java new file mode 100644 index 00000000..a5337035 --- /dev/null +++ b/examples/junit/src/test/java/com/example/AutofuzzWithCorpusFuzzTest.java @@ -0,0 +1,26 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; + +class AutofuzzWithCorpusFuzzTest { + @FuzzTest + void autofuzzWithCorpus(String str, int i) { + if ("jazzer".equals(str) && i == 1234) { + throw new RuntimeException(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/BUILD.bazel b/examples/junit/src/test/java/com/example/BUILD.bazel new file mode 100644 index 00000000..8bbdcecd --- /dev/null +++ b/examples/junit/src/test/java/com/example/BUILD.bazel @@ -0,0 +1,232 @@ +load("//bazel:fuzz_target.bzl", "java_fuzz_target_test") + +java_binary( + name = "ExampleFuzzTests", + testonly = True, + srcs = glob(["*.java"]), + create_executable = False, + visibility = [ + "//src/test/java/com/code_intelligence/jazzer/junit:__pkg__", + ], + deps = [ + "//deploy:jazzer", + "//deploy:jazzer-api", + "//deploy:jazzer-junit", + "//examples/junit/src/main/java/com/example:parser", + "//examples/junit/src/test/resources:example_seed_corpora", + "@maven//:org_junit_jupiter_junit_jupiter_api", + "@maven//:org_junit_jupiter_junit_jupiter_params", + "@maven//:org_mockito_mockito_core", + ], +) + +java_fuzz_target_test( + name = "DataFuzzTest", + srcs = ["ValidFuzzTests.java"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"], + fuzzer_args = [ + "-runs=0", + ], + target_class = "com.example.ValidFuzzTests", + target_method = "dataFuzz", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//examples/junit/src/main/java/com/example:parser", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +java_fuzz_target_test( + name = "ByteFuzzTest", + srcs = ["ByteFuzzTest.java"], + allowed_findings = ["org.opentest4j.AssertionFailedError"], + fuzzer_args = [ + "-runs=0", + ], + target_class = "com.example.ByteFuzzTest", + target_method = "byteFuzz", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//examples/junit/src/main/java/com/example:parser", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +java_fuzz_target_test( + name = "LifecycleFuzzTest", + srcs = ["LifecycleFuzzTest.java"], + allowed_findings = ["java.io.IOException"], + fuzzer_args = [ + "-runs=0", + ], + target_class = "com.example.LifecycleFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//examples/junit/src/main/java/com/example:parser", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +java_fuzz_target_test( + name = "KeepGoingFuzzTest", + srcs = ["KeepGoingFuzzTest.java"], + allowed_findings = ["java.lang.IllegalArgumentException"], + expect_crash = False, + fuzzer_args = [ + "--keep_going=3", + "-runs=10", + ], + target_class = "com.example.KeepGoingFuzzTest", + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +# Verifies that fuzzer command-line arguments are honored for @FuzzTests. +java_fuzz_target_test( + name = "CommandLineFuzzTest", + srcs = ["CommandLineFuzzTest.java"], + allowed_findings = ["java.lang.Error"], + fuzzer_args = [ + # Ignore the first two findings. + "--ignore=d5e250a5298b81e6,d86371e6d41739ec", + ], + target_class = "com.example.CommandLineFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//examples/junit/src/main/java/com/example:parser", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +# Verify that Mockito is properly ignored. +# Using version 5+ could otherwise introduce cyclic instrumentation. +java_fuzz_target_test( + name = "MockitoFuzzTest", + srcs = ["MockitoFuzzTest.java"], + fuzzer_args = [ + "-runs=1", + ], + tags = ["no-jdk8"], + target_class = "com.example.MockitoFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//examples/junit/src/main/java/com/example:parser", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + "@maven//:org_mockito_mockito_core", + ], +) + +java_fuzz_target_test( + name = "AutofuzzLifecycleFuzzTest", + srcs = ["AutofuzzLifecycleFuzzTest.java"], + fuzzer_args = [ + "-runs=0", + ], + target_class = "com.example.AutofuzzLifecycleFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +java_fuzz_target_test( + name = "MutatorFuzzTest", + srcs = ["MutatorFuzzTest.java"], + allowed_findings = ["java.lang.AssertionError"], + data = [ + "//examples/junit/src/test/resources:MutatorFuzzTestInputs", + ], + env = { + "JAZZER_FUZZ": "1", + }, + fuzzer_args = [ + "--experimental_mutator", + "$(rlocationpaths //examples/junit/src/test/resources:MutatorFuzzTestInputs)", + ], + target_class = "com.example.MutatorFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/driver:fuzz_target_runner", + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "//src/main/java/com/code_intelligence/jazzer/mutation/annotation", + "@maven//:org_junit_jupiter_junit_jupiter_api", + ], +) + +java_fuzz_target_test( + name = "JavaSeedFuzzTest", + srcs = ["JavaSeedFuzzTest.java"], + allowed_findings = ["java.lang.Error"], + env = {"JAZZER_FUZZ": "1"}, + fuzzer_args = [ + "--experimental_mutator", + ], + target_class = "com.example.JavaSeedFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "//src/main/java/com/code_intelligence/jazzer/mutation/annotation", + "@maven//:org_junit_jupiter_junit_jupiter_api", + "@maven//:org_junit_jupiter_junit_jupiter_params", + ], +) + +java_fuzz_target_test( + name = "JavaBinarySeedFuzzTest", + srcs = ["JavaBinarySeedFuzzTest.java"], + allowed_findings = ["java.lang.Error"], + env = {"JAZZER_FUZZ": "1"}, + target_class = "com.example.JavaBinarySeedFuzzTest", + verify_crash_reproducer = False, + runtime_deps = [ + ":junit_runtime", + ], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/junit:fuzz_test", + "@maven//:org_junit_jupiter_junit_jupiter_api", + "@maven//:org_junit_jupiter_junit_jupiter_params", + ], +) + +java_library( + name = "junit_runtime", + runtime_deps = [ + "@maven//:org_junit_jupiter_junit_jupiter_engine", + "@maven//:org_junit_platform_junit_platform_launcher", + ], +) diff --git a/examples/junit/src/test/java/com/example/ByteFuzzTest.java b/examples/junit/src/test/java/com/example/ByteFuzzTest.java new file mode 100644 index 00000000..506ef892 --- /dev/null +++ b/examples/junit/src/test/java/com/example/ByteFuzzTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import static org.junit.jupiter.api.Assertions.fail; + +import com.code_intelligence.jazzer.junit.FuzzTest; + +class ByteFuzzTest { + @FuzzTest + void byteFuzz(byte[] data) { + if (data.length < 1) { + return; + } + if (data[0] % 2 == 0) { + fail(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/CommandLineFuzzTest.java b/examples/junit/src/test/java/com/example/CommandLineFuzzTest.java new file mode 100644 index 00000000..e79d4638 --- /dev/null +++ b/examples/junit/src/test/java/com/example/CommandLineFuzzTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.code_intelligence.jazzer.junit.FuzzTest; + +class CommandLineFuzzTest { + int run = 0; + + @FuzzTest + void commandLineFuzz(byte[] bytes) { + assumeTrue(bytes.length > 0); + switch (run++) { + case 0: + throw new RuntimeException(); + case 1: + throw new IllegalStateException(); + case 2: + throw new Error(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/CorpusDirectoryFuzzTest.java b/examples/junit/src/test/java/com/example/CorpusDirectoryFuzzTest.java new file mode 100644 index 00000000..465c94cf --- /dev/null +++ b/examples/junit/src/test/java/com/example/CorpusDirectoryFuzzTest.java @@ -0,0 +1,45 @@ +// Copyright 2023 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.junit.FuzzTest; + +public class CorpusDirectoryFuzzTest { + private static int invocations = 0; + + @FuzzTest(maxDuration = "5s") + public void corpusDirectoryFuzz(FuzzedDataProvider data) { + // Throw on the third invocation to generate corpus entries. + if (data.remainingBytes() == 0) { + return; + } + // Add a few branch statements to generate different coverage. + switch (invocations) { + case 0: + invocations++; + break; + case 1: + invocations++; + break; + case 2: + invocations++; + break; + case 3: + throw new FuzzerSecurityIssueMedium(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/DirectoryInputsFuzzTest.java b/examples/junit/src/test/java/com/example/DirectoryInputsFuzzTest.java new file mode 100644 index 00000000..1d1ce2c4 --- /dev/null +++ b/examples/junit/src/test/java/com/example/DirectoryInputsFuzzTest.java @@ -0,0 +1,39 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.junit.FuzzTest; + +public class DirectoryInputsFuzzTest { + private static boolean firstSeed = true; + + @FuzzTest(maxDuration = "0s") + public void inputsFuzz(FuzzedDataProvider data) { + // Only execute the fuzz test logic on the empty input and the only seed. + if (data.remainingBytes() == 0) { + return; + } + String input = data.consumeRemainingAsString(); + if (!firstSeed && !input.equals("directory")) { + throw new IllegalStateException("Should have crashed on the first non-empty input"); + } + firstSeed = false; + if (input.equals("directory")) { + throw new FuzzerSecurityIssueMedium(); + } + } +} diff --git a/examples/junit/src/test/java/com/example/HermeticInstrumentationFuzzTest.java b/examples/junit/src/test/java/com/example/HermeticInstrumentationFuzzTest.java new file mode 100644 index 00000000..97e03811 --- /dev/null +++ b/examples/junit/src/test/java/com/example/HermeticInstrumentationFuzzTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2022 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; + +@SuppressWarnings("InvalidPatternSyntax") +@Execution(ExecutionMode.CONCURRENT) +class HermeticInstrumentationFuzzTest { + class VulnerableFuzzClass { + public void vulnerableMethod(String input) { + Pattern.compile(input); + } + } + + class VulnerableUnitClass { + public void vulnerableMethod(String input) { + Pattern.compile(input); + } + } + + @FuzzTest + @Execution(ExecutionMode.CONCURRENT) + void fuzzTest1(byte[] data) { + new VulnerableFuzzClass().vulnerableMethod("["); + } + + @Test + @Execution(ExecutionMode.CONCURRENT) + void unitTest1() { + new VulnerableUnitClass().vulnerableMethod("["); + } + + @FuzzTest + @Execution(ExecutionMode.CONCURRENT) + void fuzzTest2(byte[] data) { + Pattern.compile("["); + } + + @Test + @Execution(ExecutionMode.CONCURRENT) + void unitTest2() { + Pattern.compile("["); + } +} diff --git a/examples/junit/src/test/java/com/example/InvalidFuzzTests.java b/examples/junit/src/test/java/com/example/InvalidFuzzTests.java new file mode 100644 index 00000000..acaecb8c --- /dev/null +++ b/examples/junit/src/test/java/com/example/InvalidFuzzTests.java @@ -0,0 +1,23 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; + +class InvalidFuzzTests { + @FuzzTest + void invalidParameterCountFuzz() {} +} diff --git a/examples/junit/src/test/java/com/example/JavaBinarySeedFuzzTest.java b/examples/junit/src/test/java/com/example/JavaBinarySeedFuzzTest.java new file mode 100644 index 00000000..70b35352 --- /dev/null +++ b/examples/junit/src/test/java/com/example/JavaBinarySeedFuzzTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import org.junit.jupiter.params.converter.ArgumentConversionException; +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.SimpleArgumentConverter; +import org.junit.jupiter.params.provider.ValueSource; + +class JavaBinarySeedFuzzTest { + // Generated via: + // printf 'tH15_1S-4_53Cr3T.fl4G' | openssl dgst -binary -sha256 | openssl base64 -A + // Luckily the fuzzer can't read comments ;-) + private static final byte[] FLAG_SHA256 = + Base64.getDecoder().decode("q0vPdz5oeJIW3k2U4VJ+aWDufzzZbKAcevc9cNoUTSM="); + + static class Utf8BytesConverter extends SimpleArgumentConverter { + @Override + protected Object convert(Object source, Class<?> targetType) + throws ArgumentConversionException { + assertEquals(byte[].class, targetType); + assertTrue(source instanceof byte[] || source instanceof String); + if (source instanceof byte[]) { + return source; + } + return ((String) source).getBytes(UTF_8); + } + } + + @ValueSource(strings = {"red herring", "tH15_1S-4_53Cr3T.fl4Ga"}) + @FuzzTest + void fuzzTheFlag(@ConvertWith(Utf8BytesConverter.class) byte[] bytes) + throws NoSuchAlgorithmException { + assumeTrue(bytes.length > 0); + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + digest.update(bytes, 0, bytes.length - 1); + byte[] hash = digest.digest(); + byte secret = bytes[bytes.length - 1]; + if (MessageDigest.isEqual(hash, FLAG_SHA256) && secret == 's') { + throw new Error("Fl4g 4nd s3cr3et f0und!"); + } + } +} diff --git a/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java b/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java new file mode 100644 index 00000000..4f63e9a1 --- /dev/null +++ b/examples/junit/src/test/java/com/example/JavaSeedFuzzTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class JavaSeedFuzzTest { + // Generated via: + // printf 'tH15_1S-4_53Cr3T.fl4G' | openssl dgst -binary -sha256 | openssl base64 -A + // Luckily the fuzzer can't read comments ;-) + private static final byte[] FLAG_SHA256 = + Base64.getDecoder().decode("q0vPdz5oeJIW3k2U4VJ+aWDufzzZbKAcevc9cNoUTSM="); + + static Stream<Arguments> fuzzTheFlag() { + return Stream.of(arguments(asList("red", "herring"), 0), + // This argument passes the hash check, but does not trigger the finding right away. This + // is meant to verify that the seed ends up in the corpus, serving as the base for future + // mutations rather than just being executed once. + arguments(asList("tH15_1S", "-4_53Cr3T", ".fl4G"), 42)); + } + + @MethodSource + @FuzzTest + void fuzzTheFlag(@NotNull List<@NotNull String> flagParts, int secret) + throws NoSuchAlgorithmException { + byte[] hash = MessageDigest.getInstance("SHA-256").digest( + String.join("", flagParts).getBytes(StandardCharsets.UTF_8)); + if (MessageDigest.isEqual(hash, FLAG_SHA256) && secret == 1337) { + throw new Error("Fl4g 4nd s3cr3et f0und!"); + } + } +} diff --git a/examples/junit/src/test/java/com/example/KeepGoingFuzzTest.java b/examples/junit/src/test/java/com/example/KeepGoingFuzzTest.java new file mode 100644 index 00000000..ad5d09d2 --- /dev/null +++ b/examples/junit/src/test/java/com/example/KeepGoingFuzzTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; + +public class KeepGoingFuzzTest { + private static int counter = 0; + + @FuzzTest + public void keepGoingFuzzTest(byte[] ignored) { + counter++; + if (counter == 1) { + throw new IllegalArgumentException("error1"); + } + if (counter == 2) { + throw new IllegalArgumentException("error2"); + } + } +} diff --git a/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java b/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java new file mode 100644 index 00000000..0d5dc2c7 --- /dev/null +++ b/examples/junit/src/test/java/com/example/LifecycleFuzzTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.io.IOException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; + +@TestMethodOrder(MethodOrderer.MethodName.class) +@ExtendWith(LifecycleFuzzTest.LifecycleInstancePostProcessor.class) +class LifecycleFuzzTest { + // In fuzzing mode, the test is invoked once on the empty input and once with Jazzer. + private static final int EXPECTED_EACH_COUNT = + System.getenv().getOrDefault("JAZZER_FUZZ", "").isEmpty() ? 1 : 2; + + private static int beforeAllCount = 0; + private static int beforeEachGlobalCount = 0; + private static int afterEachGlobalCount = 0; + private static int afterAllCount = 0; + + private boolean beforeEachCalledOnInstance = false; + private boolean testInstancePostProcessorCalledOnInstance = false; + + @BeforeAll + static void beforeAll() { + beforeAllCount++; + } + + @BeforeEach + void beforeEach() { + beforeEachGlobalCount++; + beforeEachCalledOnInstance = true; + } + + @Disabled + @FuzzTest + void disabledFuzz(byte[] data) { + throw new AssertionError("This test should not be executed"); + } + + @FuzzTest(maxDuration = "1s") + void lifecycleFuzz(byte[] data) { + Assertions.assertEquals(1, beforeAllCount); + Assertions.assertEquals(beforeEachGlobalCount, afterEachGlobalCount + 1); + Assertions.assertTrue(beforeEachCalledOnInstance); + Assertions.assertTrue(testInstancePostProcessorCalledOnInstance); + } + + @AfterEach + void afterEach() { + afterEachGlobalCount++; + } + + @AfterAll + static void afterAll() throws IOException { + afterAllCount++; + Assertions.assertEquals(1, beforeAllCount); + Assertions.assertEquals(EXPECTED_EACH_COUNT, beforeEachGlobalCount); + Assertions.assertEquals(EXPECTED_EACH_COUNT, afterEachGlobalCount); + Assertions.assertEquals(1, afterAllCount); + throw new IOException(); + } + + static class LifecycleInstancePostProcessor implements TestInstancePostProcessor { + @Override + public void postProcessTestInstance(Object o, ExtensionContext extensionContext) { + ((LifecycleFuzzTest) o).testInstancePostProcessorCalledOnInstance = true; + } + } +} diff --git a/examples/junit/src/test/java/com/example/MockitoFuzzTest.java b/examples/junit/src/test/java/com/example/MockitoFuzzTest.java new file mode 100644 index 00000000..c3c2e973 --- /dev/null +++ b/examples/junit/src/test/java/com/example/MockitoFuzzTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.junit.FuzzTest; +import org.mockito.Mockito; + +public class MockitoFuzzTest { + public static class Foo { + public String bar(String ignored) { + return "bar"; + } + } + + @FuzzTest + void fuzzWithMockito(byte[] bytes) { + // Mock the Foo class to trigger an instrumentation cycle, + // if not properly ignored. + Foo foo = Mockito.mock(Foo.class); + foo.bar(new String(bytes)); + } +} diff --git a/examples/junit/src/test/java/com/example/MutatorFuzzTest.java b/examples/junit/src/test/java/com/example/MutatorFuzzTest.java new file mode 100644 index 00000000..f3644791 --- /dev/null +++ b/examples/junit/src/test/java/com/example/MutatorFuzzTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.code_intelligence.jazzer.driver.FuzzTargetRunner; +import com.code_intelligence.jazzer.junit.FuzzTest; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; +import java.util.List; +import org.junit.jupiter.api.AfterAll; + +class MutatorFuzzTest { + @FuzzTest + void mutatorFuzz(List<@NotNull String> list) { + // Check that the mutator is actually doing something. + if (list != null && list.size() > 3 && list.get(2).equals("mutator")) { + throw new AssertionError("Found expected JUnit mutator test issue"); + } + } + + @AfterAll + static void assertFuzzTargetRunner() { + // FuzzTargetRunner values are not set in JUnit engine tests. + String jazzerFuzz = System.getenv("JAZZER_FUZZ"); + if (jazzerFuzz != null && !jazzerFuzz.isEmpty()) { + assertTrue(FuzzTargetRunner.invalidCorpusFilesPresent()); + assertEquals(FuzzTargetRunner.mutatorDebugString(), "Arguments[Nullable<List<String>>]"); + } + } +} diff --git a/examples/junit/src/test/java/com/example/ThrowingFuzzTest.java b/examples/junit/src/test/java/com/example/ThrowingFuzzTest.java new file mode 100644 index 00000000..eabfb85d --- /dev/null +++ b/examples/junit/src/test/java/com/example/ThrowingFuzzTest.java @@ -0,0 +1,25 @@ +// Copyright 2023 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; + +public class ThrowingFuzzTest { + @FuzzTest + public void throwingFuzz(FuzzedDataProvider ignored) { + throw new IllegalStateException("This is a test."); + } +} diff --git a/examples/junit/src/test/java/com/example/ValidFuzzTests.java b/examples/junit/src/test/java/com/example/ValidFuzzTests.java new file mode 100644 index 00000000..465d3b7e --- /dev/null +++ b/examples/junit/src/test/java/com/example/ValidFuzzTests.java @@ -0,0 +1,76 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed 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. + +package com.example; + +import static org.junit.jupiter.api.Assertions.fail; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.io.IOException; +import java.util.regex.Pattern; + +@SuppressWarnings("InvalidPatternSyntax") +class ValidFuzzTests { + @FuzzTest + void dataFuzz(FuzzedDataProvider data) { + switch (data.consumeRemainingAsString()) { + case "no_crash": + return; + case "assert": + fail("JUnit assert failed"); + case "honeypot": + try { + Class.forName("jaz.Zer").newInstance(); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + // Ignored, but the honeypot class should still throw an exception. + } + case "sanitizer_internal_class": + try { + new ProcessBuilder("jazze").start(); + } catch (IOException ignored) { + // Ignored, but the sanitizer should still throw an exception. + } + case "sanitizer_user_class": + try { + Pattern.compile("["); + } catch (Throwable ignored) { + // Ignored, but the JUnit test should report an error even though all throwables are + // caught - just like Jazzer would. + } + case "": + default: + throw new FuzzerSecurityIssueMedium(); + } + } + + @FuzzTest + void byteFuzz(byte[] data) { + if (data.length < 1) { + return; + } + if (data[0] % 2 == 0) { + fail(); + } + } + + @FuzzTest(maxDuration = "10s") + void noCrashFuzz(byte[] data) { + if (data.length < 10) { + return; + } + Parser.parse(data); + } +} diff --git a/examples/junit/src/test/java/com/example/ValueProfileFuzzTest.java b/examples/junit/src/test/java/com/example/ValueProfileFuzzTest.java new file mode 100644 index 00000000..e69562f3 --- /dev/null +++ b/examples/junit/src/test/java/com/example/ValueProfileFuzzTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Code Intelligence GmbH + * + * Licensed 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.junit.FuzzTest; +import java.util.Base64; + +class ValueProfileFuzzTest { + // Only passed with the configuration parameter jazzer.valueprofile=true. + @FuzzTest(maxDuration = "20s") + void valueProfileFuzz(byte[] data) { + // Trigger some coverage even with value profiling disabled. + if (data.length < 1 || data[0] > 100) { + return; + } + if (base64(data).equals("SmF6emVy")) { + throw new FuzzerSecurityIssueMedium(); + } + } + + private static String base64(byte[] input) { + return Base64.getEncoder().encodeToString(input); + } +} |