27
27
import java .util .Optional ;
28
28
import java .util .concurrent .ThreadLocalRandom ;
29
29
import java .util .concurrent .atomic .AtomicReference ;
30
+ import java .util .function .Predicate ;
30
31
import java .util .function .Supplier ;
31
32
32
33
/**
36
37
*
37
38
* <p>The default target is "../jazzer-traversal"."
38
39
*
39
- * <p>Users may customize a customize the target by the BugDetectors API, e.g. by {@code
40
+ * <p>Users may customize the target using the BugDetectors API, e.g. by {@code
40
41
* BugDetectors.setFilePathTraversalTarget(() -> Path.of("..", "jazzer-traversal"))}.
41
42
*
42
- * <p>This does not currently check for reading metadata from the target file.
43
+ * <p>TODO: This sanitizer does not currently check for reading metadata from the target file.
43
44
*/
44
45
public class FilePathTraversal {
45
46
public static final Path DEFAULT_TARGET = Paths .get (".." , "jazzer-traversal" );
46
47
47
48
// Set via reflection by Jazzer's BugDetectors API.
48
49
public static final AtomicReference <Supplier <Path >> target =
49
50
new AtomicReference <>(() -> DEFAULT_TARGET );
51
+ public static final AtomicReference <Predicate <Path >> checkPath =
52
+ new AtomicReference <>((Path ignored ) -> true );
50
53
51
54
// When guiding the fuzzer towards the target path, sometimes both the absolute and relative paths
52
55
// are valid. In this case, we toggle between them randomly.
@@ -271,20 +274,8 @@ private static void detectAndGuidePathTraversal(Object obj, int hookId) {
271
274
if (obj == null ) {
272
275
return ;
273
276
}
274
- Path targetPath = target .get ().get ();
275
277
276
- // Users can set the atomic function to return null to disable the sanitizer.
277
- if (targetPath == null ) {
278
- return ;
279
- }
280
- targetPath = targetPath .normalize ();
281
-
282
- Path currentDir = Paths .get ("" ).toAbsolutePath ();
283
- Path absTarget = toAbsolutePath (targetPath , currentDir ).orElse (null );
284
- Path relTarget = toRelativePath (targetPath , currentDir ).orElse (null );
285
- if (absTarget == null && relTarget == null ) {
286
- return ;
287
- }
278
+ Path targetPath = target .get ().get ();
288
279
289
280
String query ;
290
281
if (obj instanceof Path ) {
@@ -305,23 +296,54 @@ private static void detectAndGuidePathTraversal(Object obj, int hookId) {
305
296
return ;
306
297
}
307
298
299
+ Predicate <Path > checkAllowed = checkPath .get ();
300
+ boolean isPathAllowed = checkAllowed == null || checkAllowed .test (Paths .get (query ).normalize ());
301
+ if (!isPathAllowed ) {
302
+ Jazzer .reportFindingFromHook (
303
+ new FuzzerSecurityIssueCritical (
304
+ "File path traversal: "
305
+ + query
306
+ + "\n Path is not allowed by the user-defined predicate."
307
+ + "\n Current path traversal fuzzing target: "
308
+ + targetPath ));
309
+ }
310
+
311
+ // Users can set the atomic function to return null to disable the fuzzer guidance.
312
+ if (targetPath == null ) {
313
+ return ;
314
+ }
315
+ targetPath = targetPath .normalize ();
316
+
317
+ Path currentDir = Paths .get ("" ).toAbsolutePath ();
318
+ Path absTarget = toAbsolutePath (targetPath , currentDir ).orElse (null );
319
+ Path relTarget = toRelativePath (targetPath , currentDir ).orElse (null );
320
+ if (absTarget == null && relTarget == null ) {
321
+ return ;
322
+ }
323
+
308
324
if ((absTarget != null && absTarget .toString ().equals (query ))
309
325
|| (relTarget != null && relTarget .toString ().equals (query ))) {
310
326
Jazzer .reportFindingFromHook (
311
- new FuzzerSecurityIssueCritical ("File path traversal: " + query ));
327
+ new FuzzerSecurityIssueCritical (
328
+ "File path traversal: "
329
+ + query
330
+ + "\n Reached current path traversal fuzzing target: "
331
+ + targetPath ));
312
332
}
333
+
313
334
if (absTarget != null && relTarget != null ) {
314
335
if (guideTowardsAbsoluteTargetPath ) {
315
- Jazzer .guideTowardsContainment (query , relTarget .toString (), hookId );
316
- } else {
317
336
Jazzer .guideTowardsContainment (query , absTarget .toString (), hookId );
337
+ } else {
338
+ Jazzer .guideTowardsContainment (query , relTarget .toString (), hookId );
318
339
}
319
340
if (--toggleCounter <= 0 ) {
320
341
guideTowardsAbsoluteTargetPath = !guideTowardsAbsoluteTargetPath ;
321
342
toggleCounter = ThreadLocalRandom .current ().nextInt (1 , MAX_TARGET_FOCUS_COUNT + 1 );
322
343
}
323
- } else
344
+ } else {
324
345
Jazzer .guideTowardsContainment (
325
346
query , (absTarget != null ? absTarget : relTarget ).toString (), hookId );
347
+ }
326
348
}
327
349
}
0 commit comments