2727import java .util .Optional ;
2828import java .util .concurrent .ThreadLocalRandom ;
2929import java .util .concurrent .atomic .AtomicReference ;
30+ import java .util .function .Predicate ;
3031import java .util .function .Supplier ;
3132
3233/**
3637 *
3738 * <p>The default target is "../jazzer-traversal"."
3839 *
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
4041 * BugDetectors.setFilePathTraversalTarget(() -> Path.of("..", "jazzer-traversal"))}.
4142 *
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.
4344 */
4445public class FilePathTraversal {
4546 public static final Path DEFAULT_TARGET = Paths .get (".." , "jazzer-traversal" );
4647
4748 // Set via reflection by Jazzer's BugDetectors API.
4849 public static final AtomicReference <Supplier <Path >> target =
4950 new AtomicReference <>(() -> DEFAULT_TARGET );
51+ public static final AtomicReference <Predicate <Path >> checkPath =
52+ new AtomicReference <>((Path ignored ) -> true );
5053
5154 // When guiding the fuzzer towards the target path, sometimes both the absolute and relative paths
5255 // are valid. In this case, we toggle between them randomly.
@@ -271,20 +274,8 @@ private static void detectAndGuidePathTraversal(Object obj, int hookId) {
271274 if (obj == null ) {
272275 return ;
273276 }
274- Path targetPath = target .get ().get ();
275277
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 ();
288279
289280 String query ;
290281 if (obj instanceof Path ) {
@@ -305,23 +296,54 @@ private static void detectAndGuidePathTraversal(Object obj, int hookId) {
305296 return ;
306297 }
307298
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+
308324 if ((absTarget != null && absTarget .toString ().equals (query ))
309325 || (relTarget != null && relTarget .toString ().equals (query ))) {
310326 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 ));
312332 }
333+
313334 if (absTarget != null && relTarget != null ) {
314335 if (guideTowardsAbsoluteTargetPath ) {
315- Jazzer .guideTowardsContainment (query , relTarget .toString (), hookId );
316- } else {
317336 Jazzer .guideTowardsContainment (query , absTarget .toString (), hookId );
337+ } else {
338+ Jazzer .guideTowardsContainment (query , relTarget .toString (), hookId );
318339 }
319340 if (--toggleCounter <= 0 ) {
320341 guideTowardsAbsoluteTargetPath = !guideTowardsAbsoluteTargetPath ;
321342 toggleCounter = ThreadLocalRandom .current ().nextInt (1 , MAX_TARGET_FOCUS_COUNT + 1 );
322343 }
323- } else
344+ } else {
324345 Jazzer .guideTowardsContainment (
325346 query , (absTarget != null ? absTarget : relTarget ).toString (), hookId );
347+ }
326348 }
327349}
0 commit comments