diff --git a/src/main/java/run/halo/app/utils/FileUtils.java b/src/main/java/run/halo/app/utils/FileUtils.java index 302d84413..4372665d1 100644 --- a/src/main/java/run/halo/app/utils/FileUtils.java +++ b/src/main/java/run/halo/app/utils/FileUtils.java @@ -1,13 +1,17 @@ package run.halo.app.utils; +import lombok.extern.slf4j.Slf4j; import org.springframework.lang.NonNull; import org.springframework.util.Assert; +import run.halo.app.exception.ForbiddenException; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Comparator; +import java.util.zip.ZipInputStream; /** * File utilities. @@ -15,6 +19,7 @@ import java.util.Comparator; * @author johnniang * @date 4/9/19 */ +@Slf4j public class FileUtils { private FileUtils() { @@ -62,4 +67,64 @@ public class FileUtils { .map(Path::toFile) .forEach(File::delete); } + + /** + * Checks directory traversal vulnerability. + * + * @param parentPath parent path must not be null. + * @param pathToCheck path to check must not be null + */ + public static void checkDirectoryTraversal(@NonNull String parentPath, @NonNull String pathToCheck) { + checkDirectoryTraversal(Paths.get(parentPath), Paths.get(pathToCheck)); + } + + /** + * Checks directory traversal vulnerability. + * + * @param parentPath parent path must not be null. + * @param pathToCheck path to check must not be null + */ + public static void checkDirectoryTraversal(@NonNull Path parentPath, @NonNull String pathToCheck) { + checkDirectoryTraversal(parentPath, Paths.get(pathToCheck)); + } + + /** + * Checks directory traversal vulnerability. + * + * @param parentPath parent path must not be null. + * @param pathToCheck path to check must not be null + */ + public static void checkDirectoryTraversal(@NonNull Path parentPath, @NonNull Path pathToCheck) { + Assert.notNull(parentPath, "Parent path must not be null"); + Assert.notNull(pathToCheck, "Path to check must not be null"); + + if (pathToCheck.startsWith(parentPath.normalize())) { + return; + } + + throw new ForbiddenException("You cannot access " + pathToCheck).setErrorData(pathToCheck); + } + + public static void closeQuietly(InputStream inputStream) { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + // Ignore this exception + log.error("Failed to close input stream", e); + } + } + + public static void closeQuietly(ZipInputStream zipInputStream) { + try { + if (zipInputStream != null) { + zipInputStream.closeEntry(); + zipInputStream.close(); + } + } catch (IOException e) { + // Ignore this exception + log.error("Failed to close zip input stream", e); + } + } } diff --git a/src/test/java/run/halo/app/utils/DirectoryAttackTest.java b/src/test/java/run/halo/app/utils/DirectoryAttackTest.java index 4b691aa82..6e91f6d05 100644 --- a/src/test/java/run/halo/app/utils/DirectoryAttackTest.java +++ b/src/test/java/run/halo/app/utils/DirectoryAttackTest.java @@ -3,6 +3,7 @@ package run.halo.app.utils; import org.junit.Assert; import org.junit.Test; +import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -17,10 +18,9 @@ public class DirectoryAttackTest { private String userHome = System.getProperty("user.home"); @Test - public void compareDirectoryTest() { + public void compareDirectoryFailureTest() { - String workDir = userHome + "/halo-test/"; - Path workDirPath = Paths.get(workDir); + Path workDirPath = Paths.get(userHome + "/halo-test/"); Path testPath = Paths.get(userHome + "/../../etc/passwd"); @@ -30,4 +30,42 @@ public class DirectoryAttackTest { Assert.assertFalse(testPath.startsWith(workDirPath)); Assert.assertFalse(workDirPath.startsWith(testPath)); } + + @Test + public void compareDirectorySuccessfullyTest() { + Path workDirPath = Paths.get(userHome + "/halo-test/"); + + Path testPath = Paths.get(userHome + "/halo-test/test.txt"); + + System.out.println("Work directory path: " + workDirPath); + System.out.println("Test path: " + testPath); + + Assert.assertTrue(testPath.startsWith(workDirPath)); + Assert.assertFalse(workDirPath.startsWith(testPath)); + } + + + @Test + public void compareDirectorySuccessfullyTest2() { + Path workDirPath = Paths.get(userHome + "/../../etc/").normalize(); + + Path testPath = Paths.get("/etc/passwd"); + + System.out.println("Work directory path: " + workDirPath); + System.out.println("Test path: " + testPath); + + Assert.assertTrue(testPath.startsWith(workDirPath)); + Assert.assertFalse(workDirPath.startsWith(testPath)); + } + + @Test + public void getRealPathTest() { + String pathname = "/home/test/../../etc/"; + Path path = Paths.get(pathname); + + System.out.println("Path: " + path); + System.out.println("Absolute path: " + path.toAbsolutePath()); + System.out.println("Name count: " + path.getNameCount()); + System.out.println("Normalized path: " + path.normalize()); + } }