• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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