diff options
Diffstat (limited to 'examples')
72 files changed, 2453 insertions, 60 deletions
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index 599b8261..1a7da538 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -2,14 +2,13 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") load("@fmeum_rules_jni//jni:defs.bzl", "java_jni_library") load("//bazel:compat.bzl", "SKIP_ON_MACOS", "SKIP_ON_WINDOWS") load("//bazel:fuzz_target.bzl", "java_fuzz_target_test") +load("//bazel:kotlin.bzl", "ktlint") java_fuzz_target_test( name = "Autofuzz", - expected_findings = ["java.lang.ArrayIndexOutOfBoundsException"], + allowed_findings = ["java.lang.ArrayIndexOutOfBoundsException"], fuzzer_args = [ "--autofuzz=com.google.json.JsonSanitizer::sanitize", - # Exit after the first finding for testing purposes. - "--keep_going=1", ], runtime_deps = [ "@maven//:com_mikesamuel_json_sanitizer", @@ -18,13 +17,22 @@ java_fuzz_target_test( java_fuzz_target_test( name = "ExampleFuzzer", - srcs = [ - "src/main/java/com/example/ExampleFuzzer.java", - "src/main/java/com/example/ExampleFuzzerHooks.java", - ], - # Comment out the next line to keep the fuzzer running indefinitely. - hook_classes = ["com.example.ExampleFuzzerHooks"], + srcs = ["src/main/java/com/example/ExampleFuzzer.java"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"], + hook_jar = "ExampleFuzzerHooks_deploy.jar", target_class = "com.example.ExampleFuzzer", + # Does not crash due to not using the hook. + verify_crash_reproducer = False, +) + +java_binary( + name = "ExampleFuzzerHooks", + srcs = ["src/main/java/com/example/ExampleFuzzerHooks.java"], + create_executable = False, + # Comment out the next line to keep the ExampleFuzzer running indefinitely - without the hook, it will never be able + # to pass the comparison with the random number. + deploy_manifest_lines = ["Jazzer-Hook-Classes: com.example.ExampleFuzzerHooks"], + deps = ["//src/main/java/com/code_intelligence/jazzer/api:hooks"], ) java_jni_library( @@ -38,14 +46,21 @@ java_jni_library( ], visibility = ["//examples/src/main/native/com/example:__pkg__"], deps = [ - "//agent:jazzer_api_compile_only", + "//deploy:jazzer-api", ], ) java_fuzz_target_test( name = "ExampleFuzzerWithASan", - fuzzer_args = ["--jvm_args=-Djazzer.native_lib=native_asan"], - sanitizer = "address", + allowed_findings = ["native"], + env = {"EXAMPLE_NATIVE_LIB": "native_asan"}, + env_inherit = ["CC"], + fuzzer_args = [ + "--asan", + ], + # The shell launcher generated by Jazzer is killed in CI, even with codesigning disabled on the + # Java binary. This is not reproducible locally or with JDK 17. + tags = ["no-macos-x86_64-jdk8"], target_class = "com.example.ExampleFuzzerWithNative", target_compatible_with = SKIP_ON_WINDOWS, verify_crash_reproducer = False, @@ -56,8 +71,15 @@ java_fuzz_target_test( java_fuzz_target_test( name = "ExampleFuzzerWithUBSan", - fuzzer_args = ["--jvm_args=-Djazzer.native_lib=native_ubsan"], - sanitizer = "undefined", + allowed_findings = ["native"], + env = {"EXAMPLE_NATIVE_LIB": "native_ubsan"}, + env_inherit = ["CC"], + fuzzer_args = [ + "--ubsan", + ], + # The shell launcher generated by Jazzer is killed in CI, even with codesigning disabled on the + # Java binary. This is not reproducible locally or with JDK 17. + tags = ["no-macos-x86_64-jdk8"], target_class = "com.example.ExampleFuzzerWithNative", # Crashes at runtime without an error message. target_compatible_with = SKIP_ON_WINDOWS, @@ -67,14 +89,23 @@ java_fuzz_target_test( ], ) +java_binary( + name = "ExamplePathTraversalFuzzerHooks", + srcs = ["src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java"], + create_executable = False, + deploy_manifest_lines = ["Jazzer-Hook-Classes: com.example.ExamplePathTraversalFuzzerHooks"], + deps = ["//src/main/java/com/code_intelligence/jazzer/api:hooks"], +) + java_fuzz_target_test( name = "ExamplePathTraversalFuzzer", srcs = [ "src/main/java/com/example/ExamplePathTraversalFuzzer.java", - "src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java", ], - hook_classes = ["com.example.ExamplePathTraversalFuzzerHooks"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh"], + hook_jar = "ExamplePathTraversalFuzzerHooks_deploy.jar", target_class = "com.example.ExamplePathTraversalFuzzer", + verify_crash_reproducer = False, ) java_fuzz_target_test( @@ -82,7 +113,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/ExampleValueProfileFuzzer.java", ], - expected_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow"], # Comment out the next line to keep the fuzzer running indefinitely. fuzzer_args = ["-use_value_profile=1"], target_class = "com.example.ExampleValueProfileFuzzer", @@ -93,17 +124,21 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/MazeFuzzer.java", ], - expected_findings = ["com.example.MazeFuzzer$$TreasureFoundException"], + allowed_findings = ["com.example.MazeFuzzer$$TreasureFoundException"], fuzzer_args = ["-use_value_profile=1"], target_class = "com.example.MazeFuzzer", ) java_fuzz_target_test( name = "ExampleOutOfMemoryFuzzer", + timeout = "short", srcs = [ "src/main/java/com/example/ExampleOutOfMemoryFuzzer.java", ], - expected_findings = ["java.lang.OutOfMemoryError"], + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow", + "java.lang.OutOfMemoryError", + ], fuzzer_args = ["--jvm_args=-Xmx512m"], target_class = "com.example.ExampleOutOfMemoryFuzzer", ) @@ -113,7 +148,10 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/ExampleStackOverflowFuzzer.java", ], - expected_findings = ["java.lang.StackOverflowError"], + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow", + "java.lang.StackOverflowError", + ], target_class = "com.example.ExampleStackOverflowFuzzer", # Crashes with a segfault before any stack trace printing is reached. target_compatible_with = SKIP_ON_MACOS, @@ -141,16 +179,37 @@ java_fuzz_target_test( ], ) +# WARNING: This fuzz target uses a vulnerable version of Apache Commons Text, which could result in the execution +# of arbitrary code during fuzzing if executed with an older JDK. Use at your own risk. +java_fuzz_target_test( + name = "CommonsTextFuzzer", + size = "enormous", + srcs = [ + "src/main/java/com/example/CommonsTextFuzzer.java", + ], + fuzzer_args = [ + "-fork=8", + "-use_value_profile=1", + ], + tags = ["manual"], + target_class = "com.example.CommonsTextFuzzer", + verify_crash_reproducer = False, + deps = [ + "@maven//:org_apache_commons_commons_text", + ], +) + java_fuzz_target_test( name = "JpegImageParserFuzzer", size = "enormous", srcs = [ "src/main/java/com/example/JpegImageParserFuzzer.java", ], - expected_findings = ["java.lang.NegativeArraySizeException"], + allowed_findings = ["java.lang.NegativeArraySizeException"], fuzzer_args = [ "-fork=2", ], + tags = ["exclusive-if-local"], target_class = "com.example.JpegImageParserFuzzer", # The exit codes of the forked libFuzzer processes are not picked up correctly. target_compatible_with = SKIP_ON_MACOS, @@ -164,7 +223,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/GifImageParserFuzzer.java", ], - expected_findings = [ + allowed_findings = [ "java.lang.ArrayIndexOutOfBoundsException", "java.lang.IllegalArgumentException", "java.lang.OutOfMemoryError", @@ -192,7 +251,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/JsonSanitizerCrashFuzzer.java", ], - expected_findings = ["java.lang.IndexOutOfBoundsException"], + allowed_findings = ["java.lang.IndexOutOfBoundsException"], target_class = "com.example.JsonSanitizerCrashFuzzer", deps = [ "@maven//:com_mikesamuel_json_sanitizer", @@ -204,7 +263,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/JsonSanitizerDenylistFuzzer.java", ], - expected_findings = ["java.lang.AssertionError"], + allowed_findings = ["java.lang.AssertionError"], target_class = "com.example.JsonSanitizerDenylistFuzzer", deps = [ "@maven//:com_mikesamuel_json_sanitizer", @@ -219,7 +278,7 @@ java_binary( main_class = "com.code_intelligence.jazzer.replay.Replayer", runtime_deps = [ ":JsonSanitizerDenylistFuzzer_target_deploy.jar", - "//agent/src/main/java/com/code_intelligence/jazzer/replay:Replayer_deploy.jar", + "//src/main/java/com/code_intelligence/jazzer/replay:Replayer_deploy.jar", ], ) @@ -245,7 +304,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/JsonSanitizerIdempotenceFuzzer.java", ], - expected_findings = ["java.lang.AssertionError"], + allowed_findings = ["java.lang.AssertionError"], target_class = "com.example.JsonSanitizerIdempotenceFuzzer", deps = [ "@maven//:com_mikesamuel_json_sanitizer", @@ -257,7 +316,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/JsonSanitizerValidJsonFuzzer.java", ], - expected_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow"], target_class = "com.example.JsonSanitizerValidJsonFuzzer", deps = [ "@maven//:com_google_code_gson_gson", @@ -270,7 +329,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/JacksonCborFuzzer.java", ], - expected_findings = ["java.lang.NullPointerException"], + allowed_findings = ["java.lang.NullPointerException"], target_class = "com.example.JacksonCborFuzzer", deps = [ "@maven//:com_fasterxml_jackson_core_jackson_core", @@ -284,7 +343,7 @@ java_fuzz_target_test( srcs = [ "src/main/java/com/example/FastJsonFuzzer.java", ], - expected_findings = ["java.lang.NumberFormatException"], + allowed_findings = ["java.lang.NumberFormatException"], target_class = "com.example.FastJsonFuzzer", deps = [ "@maven//:com_alibaba_fastjson", @@ -297,17 +356,18 @@ kt_jvm_library( "src/main/java/com/example/KlaxonFuzzer.kt", ], deps = [ - "//agent:jazzer_api_compile_only", + "//deploy:jazzer-api", "@maven//:com_beust_klaxon", ], ) java_fuzz_target_test( name = "KlaxonFuzzer", - expected_findings = [ + allowed_findings = [ "java.lang.ClassCastException", "java.lang.IllegalStateException", "java.lang.NumberFormatException", + "java.lang.NullPointerException", ], fuzzer_args = [ "--keep_going=7", @@ -316,6 +376,47 @@ java_fuzz_target_test( runtime_deps = [":KlaxonFuzzTarget"], ) +kt_jvm_library( + name = "ExampleKotlinFuzzTarget", + srcs = [ + "src/main/java/com/example/ExampleKotlinFuzzer.kt", + ], + deps = [ + "//deploy:jazzer-api", + ], +) + +java_fuzz_target_test( + name = "ExampleKotlinFuzzer", + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium", + ], + target_class = "com.example.ExampleKotlinFuzzer", + runtime_deps = [":ExampleKotlinFuzzTarget"], +) + +kt_jvm_library( + name = "ExampleKotlinValueProfileFuzzTarget", + srcs = [ + "src/main/java/com/example/ExampleKotlinValueProfileFuzzer.kt", + ], + deps = [ + "//deploy:jazzer-api", + ], +) + +java_fuzz_target_test( + name = "ExampleKotlinValueProfileFuzzer", + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium", + ], + fuzzer_args = [ + "-use_value_profile=1", + ], + target_class = "com.example.ExampleKotlinValueProfileFuzzer", + runtime_deps = [":ExampleKotlinValueProfileFuzzTarget"], +) + java_fuzz_target_test( name = "TurboJpegFuzzer", srcs = [ @@ -327,8 +428,8 @@ java_fuzz_target_test( fuzzer_args = [ "-rss_limit_mb=8196", "--jvm_args=-Djava.library.path=../libjpeg_turbo", + "--ubsan", ], - sanitizer = "address", tags = ["manual"], target_class = "com.example.TurboJpegFuzzer", deps = [ @@ -336,11 +437,31 @@ java_fuzz_target_test( ], ) +java_fuzz_target_test( + name = "BatikTranscoderFuzzer", + srcs = [ + "src/main/java/com/example/BatikTranscoderFuzzer.java", + ], + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium", + ], + target_class = "com.example.BatikTranscoderFuzzer", + verify_crash_reproducer = False, + deps = [ + "@maven//:org_apache_xmlgraphics_batik_anim", + "@maven//:org_apache_xmlgraphics_batik_bridge", + "@maven//:org_apache_xmlgraphics_batik_css", + "@maven//:org_apache_xmlgraphics_batik_transcoder", + "@maven//:org_apache_xmlgraphics_batik_util", + ], +) + java_binary( name = "examples", create_executable = False, visibility = ["//visibility:public"], runtime_deps = [ + ":BatikTranscoderFuzzer_target_deploy.jar", ":ExampleFuzzer_target_deploy.jar", ":ExampleValueProfileFuzzer_target_deploy.jar", ":FastJsonFuzzer_target_deploy.jar", @@ -349,3 +470,5 @@ java_binary( ":JsonSanitizerDenylistFuzzer_target_deploy.jar", ], ) + +ktlint() diff --git a/examples/junit-spring-web/.gitignore b/examples/junit-spring-web/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/examples/junit-spring-web/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.jar b/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.jar Binary files differnew file mode 100644 index 00000000..c1dd12f1 --- /dev/null +++ b/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.jar diff --git a/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.properties b/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..b74bf7fc --- /dev/null +++ b/examples/junit-spring-web/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/examples/junit-spring-web/build-and-run-tests.sh b/examples/junit-spring-web/build-and-run-tests.sh new file mode 100755 index 00000000..a2c819fa --- /dev/null +++ b/examples/junit-spring-web/build-and-run-tests.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# 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. + +# Development-only. This script builds the example project against the local version of Jazzer, +# runs its unit and fuzz tests, and compares the results with expected results. + +set -e +( cd ../../ && + bazel build //... +) + +# Update jazzer version used for building this project in the pom.xml +JAZZER_VERSION=$(grep -oP '(?<=JAZZER_VERSION = ")[^"]*' ../../maven.bzl) +# Find line with "<artifactId>jazzer-junit</artifactId>" and replace the version in the next line +sed -i "/<artifactId>jazzer-junit<\/artifactId>/ {n;s/<version>.*<\/version>/<version>$JAZZER_VERSION<\/version>/}" pom.xml + +# Add locally-built Jazzer to the Maven repository +./mvnw install:install-file -Dfile=../../bazel-bin/deploy/jazzer-junit-project.jar -DpomFile=../../bazel-bin/deploy/jazzer-junit-pom.xml +./mvnw install:install-file -Dfile=../../bazel-bin/deploy/jazzer-project.jar -DpomFile=../../bazel-bin/deploy/jazzer-pom.xml +./mvnw install:install-file -Dfile=../../bazel-bin/deploy/jazzer-api-project.jar -DpomFile=../../bazel-bin/deploy/jazzer-api-pom.xml + +## Regression and unit tests +echo "[SPRINGBOOT-JUNIT]: These unit and regression fuzz tests should pass" +./mvnw test -Dtest="JunitSpringWebApplicationTests#unitTestShouldPass+fuzzTestShouldPass" + +echo "[SPRINGBOOT-JUNIT]: This regression fuzz test should fail." +# Temporarily disable exit on error. +set +e +./mvnw test -Dtest="JunitSpringWebApplicationTests#fuzzTestShouldFail" +declare -i exit_code=$? +set -e + +# Assert that the test failed with exit code 1. +if [ $exit_code -eq 1 ] +then + echo "[SPRINGBOOT-JUNIT]: Expected failing fuzz tests: continuing" +else + echo "[SPRINGBOOT-JUNIT]: Expected exit code 1, but got $exit_code" + exit 1 +fi + +## Fuzz tests +echo "[SPRINGBOOT-JUNIT]: This fuzz test should pass" +JAZZER_FUZZ=1 ./mvnw test -Dtest="JunitSpringWebApplicationTests#fuzzTestShouldPass" + +echo "[SPRINGBOOT-JUNIT]: This fuzz test should fail" +set +e +JAZZER_FUZZ=1 ./mvnw test -Dtest="JunitSpringWebApplicationTests#fuzzTestShouldFail" +declare -i exit_code=$? +set -e + +if [ $exit_code -eq 1 ] +then + echo "[SPRINGBOOT-JUNIT]: Expected failing fuzz tests: continuing" +else + echo "[SPRINGBOOT-JUNIT]: Expected exit code 1, but got $exit_code" + exit 1 +fi + +echo "[SPRINGBOOT-JUNIT]: This fuzz test using autofuzz should fail" +set +e +JAZZER_FUZZ=1 ./mvnw test -Dtest="JunitSpringWebApplicationTests#fuzzTestWithDtoShouldFail" +declare -i exit_code=$? +set -e + +if [ $exit_code -eq 1 ] +then + echo "[SPRINGBOOT-JUNIT]: Expected failing fuzz tests: continuing" +else + echo "[SPRINGBOOT-JUNIT]: Expected exit code 1, but got $exit_code" + exit 1 +fi + +## CLI tests +## Assert transitive JUnit dependencies are specified +assertDependency() { + if ./mvnw dependency:tree | grep -q "$1" + then + echo "[SPRINGBOOT-JUNIT]: Found $1 dependency in project" + else + echo "[SPRINGBOOT-JUNIT]: Did not find $1 dependency in project" + exit 1 + fi +} +assertDependency "org.junit.jupiter:junit-jupiter-api" +assertDependency "org.junit.jupiter:junit-jupiter-params" +assertDependency "org.junit.platform:junit-platform-launcher" + +# Only build project and test jars, no need for a fat-jar or test execution +./mvnw jar:jar +./mvnw jar:test-jar + +# Extract dependency locations +out=$(./mvnw dependency:build-classpath -DforceStdout) +deps=$(echo "$out" | sed '/^\[/d') + +# Directly execute Jazzer without Maven +echo "[SPRINGBOOT-JUNIT]: Direct Jazzer execution of fuzz test should pass" +java -cp "target/*:${deps}" \ + com.code_intelligence.jazzer.Jazzer \ + --target_class=com.example.JunitSpringWebApplicationTests \ + --target_method=fuzzTestShouldPass \ + --instrumentation_includes=com.example.* \ + --custom_hook_includes=com.example.* + + +echo "[SPRINGBOOT-JUNIT]: Direct Jazzer execution of fuzz test using autofuzz should fail" +set +e +JAZZER_FUZZ=1 java -cp "target/*:${deps}" \ + com.code_intelligence.jazzer.Jazzer \ + --target_class=com.example.JunitSpringWebApplicationTests \ + --target_method=fuzzTestWithDtoShouldFail \ + --instrumentation_includes=com.example.* \ + --custom_hook_includes=com.example.* +declare -i exit_code=$? +set -e + +if [ $exit_code -eq 77 ] +then + echo "[SPRINGBOOT-JUNIT]: Expected failing fuzz tests: continuing" +else + echo "[SPRINGBOOT-JUNIT]: Expected exit code 77, but got $exit_code" + exit 1 +fi + +echo "[SPRINGBOOT-JUNIT]: All tests passed" diff --git a/examples/junit-spring-web/mvnw b/examples/junit-spring-web/mvnw new file mode 100755 index 00000000..8a8fb228 --- /dev/null +++ b/examples/junit-spring-web/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# 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 +# +# https://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/junit-spring-web/mvnw.cmd b/examples/junit-spring-web/mvnw.cmd new file mode 100644 index 00000000..1d8ab018 --- /dev/null +++ b/examples/junit-spring-web/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/junit-spring-web/pom.xml b/examples/junit-spring-web/pom.xml new file mode 100644 index 00000000..b8714525 --- /dev/null +++ b/examples/junit-spring-web/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>3.0.5</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + + <groupId>com.example</groupId> + <artifactId>junit-spring-web</artifactId> + <version>0.0.1-SNAPSHOT</version> + <name>junit-spring-web</name> + <description>Demo project for Spring Boot</description> + <properties> + <java.version>11</java.version> + </properties> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.code-intelligence</groupId> + <artifactId>jazzer-junit</artifactId> + <version>0.16.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>5.9.2</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/examples/junit-spring-web/src/main/java/com/example/JunitSpringWebApplication.java b/examples/junit-spring-web/src/main/java/com/example/JunitSpringWebApplication.java new file mode 100644 index 00000000..148c4d44 --- /dev/null +++ b/examples/junit-spring-web/src/main/java/com/example/JunitSpringWebApplication.java @@ -0,0 +1,87 @@ +/* + * 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 org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class JunitSpringWebApplication { + public static final class HelloRequest { + public static final HelloRequest DEFAULT = new HelloRequest(); + + String prefix = "Hello "; + String name = "World"; + String suffix = "!"; + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + } + + @GetMapping("/hello") + public String sayHello(@RequestParam(required = false, defaultValue = "World") String name) { + return "Hello " + name; + } + + @GetMapping("/buggy-hello") + public String buggyHello(@RequestParam(required = false, defaultValue = "World") String name) + throws Error { + if (name.equals("error")) { + throw new Error("Error found!"); + } + return "Hello " + name; + } + + @PostMapping("/hello") + public String postHello(@RequestBody HelloRequest request) { + if ("error".equals(request.name)) { + throw new Error("Error found!"); + } + return request.prefix + request.name + request.suffix; + } + + public static void main(String[] args) { + SpringApplication.run(JunitSpringWebApplication.class, args); + } +} diff --git a/examples/junit-spring-web/src/main/resources/application.properties b/examples/junit-spring-web/src/main/resources/application.properties new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/examples/junit-spring-web/src/main/resources/application.properties diff --git a/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java b/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java new file mode 100644 index 00000000..2cf356f2 --- /dev/null +++ b/examples/junit-spring-web/src/test/java/com/example/JunitSpringWebApplicationTests.java @@ -0,0 +1,100 @@ +/* + * 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.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; +import com.example.JunitSpringWebApplication.HelloRequest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@WebMvcTest +public class JunitSpringWebApplicationTests { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Autowired private MockMvc mockMvc; + private boolean beforeCalled = false; + + @BeforeEach + public void beforeEach() { + beforeCalled = true; + } + + @AfterEach + public void afterEach() { + beforeCalled = false; + } + + @Test + public void unitTestShouldPass() throws Exception { + mockMvc.perform(get("/hello").param("name", "Maven")); + } + + @Test + public void unitTestShouldFail() throws Exception { + mockMvc.perform(get("/buggy-hello").param("name", "error")); + } + + @FuzzTest(maxDuration = "10s") + public void fuzzTestShouldPass(FuzzedDataProvider data) throws Exception { + if (!beforeCalled) { + throw new RuntimeException("BeforeEach was not called"); + } + + String name = data.consumeRemainingAsString(); + mockMvc.perform(get("/hello").param("name", name)); + } + + @FuzzTest(maxDuration = "10s") + public void fuzzTestShouldFail(FuzzedDataProvider data) throws Exception { + if (!beforeCalled) { + throw new RuntimeException("BeforeEach was not called"); + } + + String name = data.consumeRemainingAsString(); + mockMvc.perform(get("/buggy-hello").param("name", name)) + .andExpect(content().string(containsString(name))); + } + + @FuzzTest(maxDuration = "10s") + public void fuzzTestWithDtoShouldFail(HelloRequest helloRequest) throws Exception { + if (!beforeCalled) { + throw new RuntimeException("BeforeEach was not called"); + } + Assumptions.assumeTrue( + helloRequest != null && helloRequest.name != null && !helloRequest.name.isBlank()); + + mockMvc + .perform(post("/hello") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(helloRequest))) + .andExpect(content().string(containsString(helloRequest.name))); + } +} diff --git a/examples/junit-spring-web/src/test/resources/application.properties b/examples/junit-spring-web/src/test/resources/application.properties new file mode 100644 index 00000000..64c19670 --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/application.properties @@ -0,0 +1 @@ +logging.level.org.springframework.web=INFO diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/Test-001 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/Test-001 new file mode 100644 index 00000000..760589cb --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/Test-001 @@ -0,0 +1 @@ +error
\ No newline at end of file diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 new file mode 100644 index 00000000..760589cb --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 @@ -0,0 +1 @@ +error
\ No newline at end of file diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 Binary files differnew file mode 100644 index 00000000..58002425 --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/Test-001 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/Test-001 new file mode 100644 index 00000000..760589cb --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/Test-001 @@ -0,0 +1 @@ +error
\ No newline at end of file diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 new file mode 100644 index 00000000..760589cb --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-11f9578d05e6f7bb58a3cdd00107e9f4e3882671 @@ -0,0 +1 @@ +error
\ No newline at end of file diff --git a/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 Binary files differnew file mode 100644 index 00000000..58002425 --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/com/example/JunitSpringWebApplicationTestsInputs/fuzzTestWithDtoShouldFail/crash-4acd17b34d3dafa673ab1f7ade3a8a29582a5730 diff --git a/examples/junit-spring-web/src/test/resources/junit-platform.properties b/examples/junit-spring-web/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..02290619 --- /dev/null +++ b/examples/junit-spring-web/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +jazzer.instrument=com.example.**,com.other.package.** diff --git a/examples/junit/.gitignore b/examples/junit/.gitignore new file mode 100644 index 00000000..f419dc7b --- /dev/null +++ b/examples/junit/.gitignore @@ -0,0 +1,2 @@ +/.idea +/target diff --git a/examples/junit/pom.xml b/examples/junit/pom.xml new file mode 100644 index 00000000..cd3bae94 --- /dev/null +++ b/examples/junit/pom.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.example</groupId> + <artifactId>jazzer-junit-example</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + </properties> + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>5.9.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.code-intelligence</groupId> + <artifactId>jazzer-junit</artifactId> + <version>0.16.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.2.0</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.22.2</version> + </plugin> + </plugins> + <testResources> + <testResource> + <directory>${project.basedir}/src/test/resources</directory> + </testResource> + </testResources> + </build> + +</project> diff --git a/examples/junit/src/main/java/com/example/BUILD.bazel b/examples/junit/src/main/java/com/example/BUILD.bazel new file mode 100644 index 00000000..69654593 --- /dev/null +++ b/examples/junit/src/main/java/com/example/BUILD.bazel @@ -0,0 +1,5 @@ +java_library( + name = "parser", + srcs = ["Parser.java"], + visibility = ["//examples/junit/src/test/java/com/example:__pkg__"], +) diff --git a/examples/junit/src/main/java/com/example/Parser.java b/examples/junit/src/main/java/com/example/Parser.java new file mode 100644 index 00000000..966a0d3e --- /dev/null +++ b/examples/junit/src/main/java/com/example/Parser.java @@ -0,0 +1,25 @@ +/* + * 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; + +public class Parser { + public static void parse(byte[] data) { + if (data[4] == 'c' && new String(data).startsWith("aaaaaa")) { + throw new IllegalStateException("Not reached"); + } + } +} 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); + } +} diff --git a/examples/junit/src/test/resources/BUILD.bazel b/examples/junit/src/test/resources/BUILD.bazel new file mode 100644 index 00000000..69343053 --- /dev/null +++ b/examples/junit/src/test/resources/BUILD.bazel @@ -0,0 +1,11 @@ +java_library( + name = "example_seed_corpora", + resources = glob(["com/example/*Inputs/**"]), + visibility = ["//examples/junit/src/test/java/com/example:__pkg__"], +) + +filegroup( + name = "MutatorFuzzTestInputs", + srcs = ["com/example/MutatorFuzzTestInputs"], + visibility = ["//visibility:public"], +) diff --git a/examples/junit/src/test/resources/com/example/AutofuzzWithCorpusFuzzTestInputs/autofuzzWithCorpus/crashing_input b/examples/junit/src/test/resources/com/example/AutofuzzWithCorpusFuzzTestInputs/autofuzzWithCorpus/crashing_input Binary files differnew file mode 100644 index 00000000..2c92661f --- /dev/null +++ b/examples/junit/src/test/resources/com/example/AutofuzzWithCorpusFuzzTestInputs/autofuzzWithCorpus/crashing_input diff --git a/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/fails b/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/fails new file mode 100644 index 00000000..63d8dbd4 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/fails @@ -0,0 +1 @@ +b
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/succeeds b/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/succeeds new file mode 100644 index 00000000..2e65efe2 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ByteFuzzTestInputs/byteFuzz/succeeds @@ -0,0 +1 @@ +a
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/MutatorFuzzTestInputs/mutatorFuzz/invalid b/examples/junit/src/test/resources/com/example/MutatorFuzzTestInputs/mutatorFuzz/invalid new file mode 100644 index 00000000..acbe86c7 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/MutatorFuzzTestInputs/mutatorFuzz/invalid @@ -0,0 +1 @@ +abcd diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/assert b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/assert new file mode 100644 index 00000000..60d58a74 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/assert @@ -0,0 +1 @@ +assert
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/honeypot b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/honeypot new file mode 100644 index 00000000..ff56ae26 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/honeypot @@ -0,0 +1 @@ +honeypot
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_internal_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_internal_class new file mode 100644 index 00000000..05ead164 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_internal_class @@ -0,0 +1 @@ +sanitizer_internal_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_user_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_user_class new file mode 100644 index 00000000..067da781 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/byteFuzz/sanitizer_user_class @@ -0,0 +1 @@ +sanitizer_user_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/assert b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/assert new file mode 100644 index 00000000..60d58a74 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/assert @@ -0,0 +1 @@ +assert
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/honeypot b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/honeypot new file mode 100644 index 00000000..ff56ae26 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/honeypot @@ -0,0 +1 @@ +honeypot
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_internal_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_internal_class new file mode 100644 index 00000000..05ead164 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_internal_class @@ -0,0 +1 @@ +sanitizer_internal_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_user_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_user_class new file mode 100644 index 00000000..067da781 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/dataFuzz/sanitizer_user_class @@ -0,0 +1 @@ +sanitizer_user_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/assert b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/assert new file mode 100644 index 00000000..60d58a74 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/assert @@ -0,0 +1 @@ +assert
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/honeypot b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/honeypot new file mode 100644 index 00000000..ff56ae26 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/honeypot @@ -0,0 +1 @@ +honeypot
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_internal_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_internal_class new file mode 100644 index 00000000..05ead164 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_internal_class @@ -0,0 +1 @@ +sanitizer_internal_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_user_class b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_user_class new file mode 100644 index 00000000..067da781 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/noCrashFuzz/sanitizer_user_class @@ -0,0 +1 @@ +sanitizer_user_class
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/no_crash b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/no_crash new file mode 100644 index 00000000..65435580 --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValidFuzzTestsInputs/no_crash @@ -0,0 +1 @@ +no_crash
\ No newline at end of file diff --git a/examples/junit/src/test/resources/com/example/ValueProfileFuzzTestInputs/valueProfileFuzz/empty_seed b/examples/junit/src/test/resources/com/example/ValueProfileFuzzTestInputs/valueProfileFuzz/empty_seed new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/examples/junit/src/test/resources/com/example/ValueProfileFuzzTestInputs/valueProfileFuzz/empty_seed diff --git a/examples/junit/src/test/resources/junit-platform.properties b/examples/junit/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..02290619 --- /dev/null +++ b/examples/junit/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +jazzer.instrument=com.example.**,com.other.package.** diff --git a/examples/src/main/java/com/example/BatikTranscoderFuzzer.java b/examples/src/main/java/com/example/BatikTranscoderFuzzer.java new file mode 100644 index 00000000..cdd22163 --- /dev/null +++ b/examples/src/main/java/com/example/BatikTranscoderFuzzer.java @@ -0,0 +1,44 @@ +// 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 java.io.*; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.JPEGTranscoder; + +public class BatikTranscoderFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) throws IOException { + String host = data.consumeRemainingAsString(); + + byte[] svg = + ("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n" + + "<image width=\"50\" height=\"50\" xlink:href=\"https://" + host + "/\"></image>\n" + + "</svg>") + .getBytes(); + + // Convert SVG to JPEG + try { + JPEGTranscoder transcoder = new JPEGTranscoder(); + TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(svg)); + TranscoderOutput output = new TranscoderOutput(new ByteArrayOutputStream()); + transcoder.transcode(input, output); + } catch (TranscoderException | IllegalArgumentException e) { + // Ignored + } + } +} diff --git a/examples/src/main/java/com/example/CommonsTextFuzzer.java b/examples/src/main/java/com/example/CommonsTextFuzzer.java new file mode 100644 index 00000000..32b309d5 --- /dev/null +++ b/examples/src/main/java/com/example/CommonsTextFuzzer.java @@ -0,0 +1,28 @@ +// 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 org.apache.commons.text.StringSubstitutor; + +public class CommonsTextFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + try { + StringSubstitutor.createInterpolator().replace(data.consumeAsciiString(20)); + } catch ( + java.lang.IllegalArgumentException | java.lang.ArrayIndexOutOfBoundsException ignored) { + } + } +} diff --git a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java index b9a13e24..90639deb 100644 --- a/examples/src/main/java/com/example/ExampleFuzzerWithNative.java +++ b/examples/src/main/java/com/example/ExampleFuzzerWithNative.java @@ -19,7 +19,7 @@ import com.github.fmeum.rules_jni.RulesJni; public class ExampleFuzzerWithNative { static { - String native_lib = System.getProperty("jazzer.native_lib"); + String native_lib = System.getenv("EXAMPLE_NATIVE_LIB"); RulesJni.loadLibrary(native_lib, ExampleFuzzerWithNative.class); } diff --git a/examples/src/main/java/com/example/ExampleKotlinFuzzer.kt b/examples/src/main/java/com/example/ExampleKotlinFuzzer.kt new file mode 100644 index 00000000..eb1aea8f --- /dev/null +++ b/examples/src/main/java/com/example/ExampleKotlinFuzzer.kt @@ -0,0 +1,38 @@ +// 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 + +object ExampleKotlinFuzzer { + + @JvmStatic + fun fuzzerTestOneInput(data: FuzzedDataProvider) { + exploreMe(data.consumeString(8), data.consumeInt(), data.consumeRemainingAsString()) + } + + private fun exploreMe(prefix: String, n: Int, suffix: String) { + if (prefix.findAnyOf(arrayListOf("Fuzz", "Test")) != null) { + if (n >= 2000000) { + if (suffix.startsWith("@")) { + if (suffix.substring(1) == "Jazzer") { + throw FuzzerSecurityIssueMedium("Jazzer resolved string comparisons in Kotlin") + } + } + } + } + } +} diff --git a/examples/src/main/java/com/example/ExampleKotlinValueProfileFuzzer.kt b/examples/src/main/java/com/example/ExampleKotlinValueProfileFuzzer.kt new file mode 100644 index 00000000..c86824e8 --- /dev/null +++ b/examples/src/main/java/com/example/ExampleKotlinValueProfileFuzzer.kt @@ -0,0 +1,37 @@ +// 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 + +object ExampleKotlinValueProfileFuzzer { + + @JvmStatic + fun fuzzerTestOneInput(data: FuzzedDataProvider) { + if (data.consumeInt().compareTo(0x11223344) != 0) { + return + } + if (encrypt(data.consumeLong()).compareTo(5788627691251634856) == 0 && + encrypt(data.consumeLong()).compareTo(6293579535917519017) == 0 + ) { + throw FuzzerSecurityIssueMedium("Jazzer can handle integral comparisons in Kotlin") + } + } + + private fun encrypt(n: Long): Long { + return n.xor(0x1122334455667788) + } +} diff --git a/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java index d704da39..494bff16 100644 --- a/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java +++ b/examples/src/main/java/com/example/ExampleOutOfMemoryFuzzer.java @@ -14,15 +14,13 @@ package com.example; -import java.util.ArrayList; - public class ExampleOutOfMemoryFuzzer { + public static long[] leak; + public static void fuzzerTestOneInput(byte[] input) { - ArrayList<Byte> bytes = new ArrayList<>(); - int pos = 0; - while (pos >= 0 && pos < input.length) { - bytes.add(input[pos]); - pos += input[pos] + 1; + if (input.length == 0) { + return; } + leak = new long[Integer.MAX_VALUE]; } } diff --git a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java index b027de5b..109db94e 100644 --- a/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java +++ b/examples/src/main/java/com/example/ExamplePathTraversalFuzzerHooks.java @@ -24,6 +24,8 @@ import java.nio.file.Path; import java.nio.file.Paths; public class ExamplePathTraversalFuzzerHooks { + private static final String publicFilesRootPath = "/app/upload/"; + @MethodHook(type = HookType.BEFORE, targetClassName = "java.io.File", targetMethod = "<init>", targetMethodDescriptor = "(Ljava/lang/String;)V") public static void @@ -36,7 +38,7 @@ public class ExamplePathTraversalFuzzerHooks { // Invalid paths are correctly rejected by the application. return; } - if (!normalizedPath.startsWith(ExamplePathTraversalFuzzer.publicFilesRootPath)) { + if (!normalizedPath.startsWith(publicFilesRootPath)) { // Simply throwing an exception from here would not work as the calling code catches and // ignores all Throwables. Instead, use the Jazzer API to report a finding from a hook. Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh( diff --git a/examples/src/main/java/com/example/MazeFuzzer.java b/examples/src/main/java/com/example/MazeFuzzer.java index 9d3448c7..beab610e 100644 --- a/examples/src/main/java/com/example/MazeFuzzer.java +++ b/examples/src/main/java/com/example/MazeFuzzer.java @@ -17,6 +17,7 @@ package com.example; import com.code_intelligence.jazzer.api.Consumer3; import com.code_intelligence.jazzer.api.Jazzer; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Collectors; // A fuzz target that shows how manually informing the fuzzer about important state can make a fuzz @@ -60,7 +61,7 @@ public final class MazeFuzzer { // This is the key line that makes this fuzz target work: It instructs the fuzzer to track // every new combination of x and y as a new feature. Without it, the fuzzer would be // completely lost in the maze as guessing an escaping path by chance is close to impossible. - Jazzer.exploreState(hash(x, y), 0); + Jazzer.exploreState((byte) Objects.hash(x, y), 0); if (REACHED_FIELDS[y][x] == ' ') { // Fuzzer reached a new field in the maze, print its progress. REACHED_FIELDS[y][x] = '.'; @@ -69,18 +70,6 @@ public final class MazeFuzzer { }); } - // Hash function with good mixing properties published by Thomas Mueller - // under the terms of CC BY-SA 4.0 at - // https://stackoverflow.com/a/12996028 - // https://creativecommons.org/licenses/by-sa/4.0/ - private static byte hash(byte x, byte y) { - int h = (x << 8) | y; - h = ((h >> 16) ^ h) * 0x45d9f3b; - h = ((h >> 16) ^ h) * 0x45d9f3b; - h = (h >> 16) ^ h; - return (byte) h; - } - private static class TreasureFoundException extends RuntimeException { TreasureFoundException(byte[] commands) { super(renderPath(commands)); diff --git a/examples/src/main/native/com/example/BUILD.bazel b/examples/src/main/native/com/example/BUILD.bazel index 4c44327a..338be9ee 100644 --- a/examples/src/main/native/com/example/BUILD.bazel +++ b/examples/src/main/native/com/example/BUILD.bazel @@ -17,13 +17,14 @@ cc_jni_library( "_DISABLE_VECTOR_ANNOTATION=1", ], linkopts = select({ - "//:clang_on_linux": ["-fuse-ld=lld"], "@platforms//os:windows": [ # Windows requires all symbols that should be imported from the main # executable to be defined by an import lib. "/wholearchive:clang_rt.asan_dll_thunk-x86_64.lib", ], - "//conditions:default": [], + "//conditions:default": [ + "-fsanitize=fuzzer-no-link,address", + ], }), visibility = ["//examples:__pkg__"], deps = [ @@ -41,13 +42,14 @@ cc_jni_library( "-fno-sanitize-recover=all", ], linkopts = select({ - "//:clang_on_linux": ["-fuse-ld=lld"], "@platforms//os:windows": [ # Using the asan thunk is correct here as it contains symbols for # UBSan and SanCov as well. "/wholearchive:clang_rt.asan_dll_thunk-x86_64.lib", ], - "//conditions:default": [], + "//conditions:default": [ + "-fsanitize=fuzzer-no-link,undefined", + ], }), visibility = ["//examples:__pkg__"], deps = [ diff --git a/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp b/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp index 774e5998..971ea740 100644 --- a/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp +++ b/examples/src/main/native/com/example/com_example_ExampleFuzzerWithNative.cpp @@ -14,6 +14,7 @@ #include "com_example_ExampleFuzzerWithNative.h" +#include <cstring> #include <limits> #include <string> @@ -27,8 +28,10 @@ __attribute__((optnone)) void parseInternal(const std::string &input) { } if (input[0] == 'a' && input[1] == 'b' && input[5] == 'c') { if (input.find("secret_in_native_library") != std::string::npos) { - // Crashes with ASan. - [[maybe_unused]] char foo = input[input.size() + 2]; + // Crashes with ASan, whose use-after-free hooks detect + const char *mem = static_cast<const char *>(malloc(2)); + free((void *)mem); + [[maybe_unused]] bool foo = memcmp(mem, mem + 1, 1); } } } |