1 // Copyright 2022 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.junit; 16 17 import static com.google.common.truth.Truth.assertThat; 18 import static com.google.common.truth.Truth8.assertThat; 19 import static org.junit.Assume.assumeFalse; 20 import static org.junit.Assume.assumeTrue; 21 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; 22 import static org.junit.platform.testkit.engine.EventConditions.container; 23 import static org.junit.platform.testkit.engine.EventConditions.displayName; 24 import static org.junit.platform.testkit.engine.EventConditions.event; 25 import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; 26 import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; 27 import static org.junit.platform.testkit.engine.EventConditions.test; 28 import static org.junit.platform.testkit.engine.EventConditions.type; 29 import static org.junit.platform.testkit.engine.EventConditions.uniqueIdSubstrings; 30 import static org.junit.platform.testkit.engine.EventType.DYNAMIC_TEST_REGISTERED; 31 import static org.junit.platform.testkit.engine.EventType.FINISHED; 32 import static org.junit.platform.testkit.engine.EventType.REPORTING_ENTRY_PUBLISHED; 33 import static org.junit.platform.testkit.engine.EventType.STARTED; 34 import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; 35 36 import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; 37 import java.io.IOException; 38 import java.nio.charset.StandardCharsets; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.nio.file.Paths; 42 import java.util.stream.Stream; 43 import org.junit.Before; 44 import org.junit.Rule; 45 import org.junit.Test; 46 import org.junit.platform.testkit.engine.EngineExecutionResults; 47 import org.junit.platform.testkit.engine.EngineTestKit; 48 import org.junit.rules.TemporaryFolder; 49 50 public class DirectoryInputsTest { 51 private static final String ENGINE = "engine:junit-jupiter"; 52 private static final String CLAZZ = "class:com.example.DirectoryInputsFuzzTest"; 53 private static final String INPUTS_FUZZ = 54 "test-template:inputsFuzz(com.code_intelligence.jazzer.api.FuzzedDataProvider)"; 55 private static final String INVOCATION = "test-template-invocation:#"; 56 57 @Rule public TemporaryFolder temp = new TemporaryFolder(); 58 Path baseDir; 59 60 @Before setup()61 public void setup() { 62 baseDir = temp.getRoot().toPath(); 63 } 64 65 @Test fuzzingEnabled()66 public void fuzzingEnabled() throws IOException { 67 assumeFalse(System.getenv("JAZZER_FUZZ").isEmpty()); 68 69 // Create a fake test resource directory structure with an inputs directory to verify that 70 // Jazzer uses it and emits a crash file into it. 71 Path inputsDirectory = baseDir.resolve(Paths.get("src", "test", "resources", "com", "example", 72 "DirectoryInputsFuzzTestInputs", "inputsFuzz")); 73 Files.createDirectories(inputsDirectory); 74 75 EngineExecutionResults results = 76 EngineTestKit.engine("junit-jupiter") 77 .selectors(selectClass("com.example.DirectoryInputsFuzzTest")) 78 .configurationParameter("jazzer.internal.basedir", baseDir.toAbsolutePath().toString()) 79 .execute(); 80 81 results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), 82 event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), 83 event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 84 event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ)), 85 finishedSuccessfully()), 86 event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), 87 event(type(FINISHED), container(ENGINE), finishedSuccessfully())); 88 89 results.testEvents().assertEventsMatchExactly( 90 event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 91 event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), 92 event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), 93 displayName("<empty input>"), finishedSuccessfully()), 94 event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 95 event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), 96 event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), 97 displayName("seed"), finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class))), 98 event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 99 event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), 100 displayName("Fuzzing...")), 101 event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 3)), 102 displayName("Fuzzing..."), 103 finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); 104 105 // Should crash on the exact input "directory" as provided by the seed, with the crash emitted 106 // into the seed corpus. 107 try (Stream<Path> crashFiles = Files.list(baseDir).filter( 108 path -> path.getFileName().toString().startsWith("crash-"))) { 109 assertThat(crashFiles).isEmpty(); 110 } 111 try (Stream<Path> seeds = Files.list(inputsDirectory)) { 112 assertThat(seeds).containsExactly( 113 inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d")); 114 } 115 assertThat(Files.readAllBytes( 116 inputsDirectory.resolve("crash-8d392f56d616a516ceabb82ed8906418bce4647d"))) 117 .isEqualTo("directory".getBytes(StandardCharsets.UTF_8)); 118 119 // Verify that the engine created the generated corpus directory. Since the crash was found on a 120 // seed, it should be empty. 121 Path generatedCorpus = baseDir.resolve( 122 Paths.get(".cifuzz-corpus", "com.example.DirectoryInputsFuzzTest", "inputsFuzz")); 123 assertThat(Files.isDirectory(generatedCorpus)).isTrue(); 124 try (Stream<Path> entries = Files.list(generatedCorpus)) { 125 assertThat(entries).isEmpty(); 126 } 127 } 128 129 @Test fuzzingDisabled()130 public void fuzzingDisabled() { 131 assumeTrue(System.getenv("JAZZER_FUZZ").isEmpty()); 132 133 EngineExecutionResults results = 134 EngineTestKit.engine("junit-jupiter") 135 .selectors(selectClass("com.example.DirectoryInputsFuzzTest")) 136 .execute(); 137 138 results.containerEvents().assertEventsMatchExactly(event(type(STARTED), container(ENGINE)), 139 event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ))), 140 event(type(STARTED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 141 event(type(REPORTING_ENTRY_PUBLISHED), 142 container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 143 event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 144 event(type(FINISHED), container(uniqueIdSubstrings(ENGINE, CLAZZ)), finishedSuccessfully()), 145 event(type(FINISHED), container(ENGINE), finishedSuccessfully())); 146 147 results.testEvents().assertEventsMatchExactly( 148 event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 149 event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1))), 150 event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 1)), 151 displayName("<empty input>"), finishedSuccessfully()), 152 event(type(DYNAMIC_TEST_REGISTERED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ))), 153 event(type(STARTED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2))), 154 event(type(FINISHED), test(uniqueIdSubstrings(ENGINE, CLAZZ, INPUTS_FUZZ, INVOCATION + 2)), 155 displayName("seed"), finishedWithFailure(instanceOf(FuzzerSecurityIssueMedium.class)))); 156 157 // Verify that the generated corpus directory hasn't been created. 158 Path generatedCorpus = 159 baseDir.resolve(Paths.get(".cifuzz-corpus", "com.example.DirectoryInputsFuzzTest")); 160 assertThat(Files.notExists(generatedCorpus)).isTrue(); 161 } 162 } 163