diff --git a/src/main/java/stirling/software/SPDF/utils/FileToPdf.java b/src/main/java/stirling/software/SPDF/utils/FileToPdf.java index 6a0e263aa..dbc0915bb 100644 --- a/src/main/java/stirling/software/SPDF/utils/FileToPdf.java +++ b/src/main/java/stirling/software/SPDF/utils/FileToPdf.java @@ -169,7 +169,7 @@ public class FileToPdf { } } - // search for the main HTML file. + // Search for the main HTML file. try (Stream walk = Files.walk(tempDirectory)) { List htmlFiles = walk.filter(file -> file.toString().endsWith(".html")) @@ -190,46 +190,20 @@ public class FileToPdf { } } - public static byte[] convertBookTypeToPdf(byte[] bytes, String originalFilename) - throws IOException, InterruptedException { - if (originalFilename == null || originalFilename.lastIndexOf('.') == -1) { - throw new IllegalArgumentException("Invalid original filename."); - } - - String fileExtension = originalFilename.substring(originalFilename.lastIndexOf('.')); - List command = new ArrayList<>(); - Path tempOutputFile = Files.createTempFile("output_", ".pdf"); - Path tempInputFile = null; - - try { - // Create temp file with appropriate extension - tempInputFile = Files.createTempFile("input_", fileExtension); - Files.write(tempInputFile, bytes); - - command.add("ebook-convert"); - command.add(tempInputFile.toString()); - command.add(tempOutputFile.toString()); - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.CALIBRE) - .runCommandWithOutputHandling(command); - - return Files.readAllBytes(tempOutputFile); - } finally { - // Clean up temporary files - if (tempInputFile != null) { - Files.deleteIfExists(tempInputFile); - } - Files.deleteIfExists(tempOutputFile); - } - } - static String sanitizeZipFilename(String entryName) { if (entryName == null || entryName.trim().isEmpty()) { - return entryName; + return ""; } + // Remove any drive letters (e.g., "C:\") and leading forward/backslashes + entryName = entryName.replaceAll("^[a-zA-Z]:[\\\\/]+", ""); + entryName = entryName.replaceAll("^[\\\\/]+", ""); + + // Recursively remove path traversal sequences while (entryName.contains("../") || entryName.contains("..\\")) { entryName = entryName.replace("../", "").replace("..\\", ""); } + // Normalize all backslashes to forward slashes + entryName = entryName.replaceAll("\\\\", "/"); return entryName; } } diff --git a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java index a8d399697..e5b8fbb36 100644 --- a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java +++ b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java @@ -1,6 +1,10 @@ package stirling.software.SPDF.utils; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.InterruptedIOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -222,7 +226,7 @@ public class ProcessExecutor { boolean isQpdf = command != null && !command.isEmpty() && command.get(0).contains("qpdf"); - if (outputLines.size() > 0) { + if (!outputLines.isEmpty()) { String outputMessage = String.join("\n", outputLines); messages += outputMessage; if (!liveUpdates) { @@ -230,7 +234,7 @@ public class ProcessExecutor { } } - if (errorLines.size() > 0) { + if (!errorLines.isEmpty()) { String errorMessage = String.join("\n", errorLines); messages += errorMessage; if (!liveUpdates) { diff --git a/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java b/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java index f5cb2c802..8edb1b871 100644 --- a/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java +++ b/src/test/java/stirling/software/SPDF/utils/FileToPdfTest.java @@ -5,31 +5,79 @@ import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; public class FileToPdfTest { + /** + * Test the HTML to PDF conversion. + * This test expects an IOException when an empty HTML input is provided. + */ @Test public void testConvertHtmlToPdf() { HTMLToPdfRequest request = new HTMLToPdfRequest(); - byte[] fileBytes = new byte[0]; // Sample file bytes - String fileName = "test.html"; // Sample file name - boolean disableSanitize = false; // Sample boolean value + byte[] fileBytes = new byte[0]; // Sample file bytes (empty input) + String fileName = "test.html"; // Sample file name indicating an HTML file + boolean disableSanitize = false; // Flag to control sanitization - // Check if the method throws IOException - assertThrows(IOException.class, () -> { - FileToPdf.convertHtmlToPdf("/path/",request, fileBytes, fileName, disableSanitize); - }); + // Expect an IOException to be thrown due to empty input + Throwable thrown = + assertThrows( + IOException.class, + () -> + FileToPdf.convertHtmlToPdf( + "/path/", request, fileBytes, fileName, disableSanitize)); + assertNotNull(thrown); } + /** + * Test sanitizeZipFilename with null or empty input. + * It should return an empty string in these cases. + */ @Test - public void testConvertBookTypeToPdf() { - byte[] bytes = new byte[10]; // Sample bytes - String originalFilename = "test.epub"; // Sample original filename + public void testSanitizeZipFilename_NullOrEmpty() { + assertEquals("", FileToPdf.sanitizeZipFilename(null)); + assertEquals("", FileToPdf.sanitizeZipFilename(" ")); + } - // Check if the method throws IOException - assertThrows(IOException.class, () -> { - FileToPdf.convertBookTypeToPdf(bytes, originalFilename); - }); + /** + * Test sanitizeZipFilename to ensure it removes path traversal sequences. + * This includes removing both forward and backward slash sequences. + */ + @Test + public void testSanitizeZipFilename_RemovesTraversalSequences() { + String input = "../some/../path/..\\to\\file.txt"; + String expected = "some/path/to/file.txt"; + + // Print output for debugging purposes + System.out.println("sanitizeZipFilename " + FileToPdf.sanitizeZipFilename(input)); + System.out.flush(); + + // Expect that the method replaces backslashes with forward slashes + // and removes path traversal sequences + assertEquals(expected, FileToPdf.sanitizeZipFilename(input)); + } + + /** + * Test sanitizeZipFilename to ensure that it removes leading drive letters and slashes. + */ + @Test + public void testSanitizeZipFilename_RemovesLeadingDriveAndSlashes() { + String input = "C:\\folder\\file.txt"; + String expected = "folder/file.txt"; + assertEquals(expected, FileToPdf.sanitizeZipFilename(input)); + + input = "/folder/file.txt"; + expected = "folder/file.txt"; + assertEquals(expected, FileToPdf.sanitizeZipFilename(input)); + } + + /** + * Test sanitizeZipFilename to verify that safe filenames remain unchanged. + */ + @Test + public void testSanitizeZipFilename_NoChangeForSafeNames() { + String input = "folder/subfolder/file.txt"; + assertEquals(input, FileToPdf.sanitizeZipFilename(input)); } }