From 218d21f07a96a49ab37e1bb95488c0646a39c001 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Tue, 20 May 2025 12:02:10 +0100 Subject: [PATCH] Update AGENTS guidelines (#3556) ## Summary - clarify Codex contribution instructions - remove `test.sh` reference and require `./gradlew build` - add Developer Guide, AI note and translation policy ## Testing - `./gradlew spotlessApply` - `./gradlew build` --- AGENTS.md | 24 ++ .../SPDF/EE/KeygenLicenseVerifier.java | 210 +++++++++++------- .../service/CustomPDFDocumentFactoryTest.java | 104 ++++----- .../SPDF/service/SpyPDFDocumentFactory.java | 16 +- .../SPDF/utils/CustomHtmlSanitizerTest.java | 41 ++-- .../software/SPDF/utils/PDFToFileTest.java | 12 +- 6 files changed, 229 insertions(+), 178 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..461d26c07 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,24 @@ +# Codex Contribution Guidelines for Stirling-PDF + +This file provides high-level instructions for Codex when modifying any files within this repository. Follow these rules to ensure changes remain consistent with the existing project structure. + +## 1. Code Style and Formatting +- Respect the `.editorconfig` settings located in the repository root. Java files use 4 spaces; HTML, JS, and Python generally use 2 spaces. Lines should end with `LF`. +- Format Java code with `./gradlew spotlessApply` before committing. +- Review `DeveloperGuide.md` for project structure and design details before making significant changes. + +## 2. Testing +- Run `./gradlew build` before committing changes to ensure the project compiles. +- If the build cannot complete due to environment restrictions, DO NOT COMMIT THE CHANGE + +## 3. Commits +- Keep commits focused. Group related changes together and provide concise commit messages. +- Ensure the working tree is clean (`git status`) before concluding your work. + +## 4. Pull Requests +- Summarize what was changed and why. Include build results from `./gradlew build` in the PR description. +- Note that the code was generated with the assistance of AI. + +## 5. Translations +- Only modify `messages_en_GB.properties` when adding or updating translations. + diff --git a/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java b/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java index 2be506bec..092665dc3 100644 --- a/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java +++ b/src/main/java/stirling/software/SPDF/EE/KeygenLicenseVerifier.java @@ -47,19 +47,20 @@ public class KeygenLicenseVerifier { private static final ObjectMapper objectMapper = new ObjectMapper(); private final ApplicationProperties applicationProperties; - + // Shared HTTP client for connection pooling - private static final HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .connectTimeout(java.time.Duration.ofSeconds(10)) - .build(); - + private static final HttpClient httpClient = + HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(java.time.Duration.ofSeconds(10)) + .build(); + // License metadata context class to avoid shared mutable state private static class LicenseContext { private boolean isFloatingLicense = false; private int maxMachines = 1; // Default to 1 if not specified private boolean isEnterpriseLicense = false; - + public LicenseContext() {} } @@ -248,7 +249,7 @@ public class KeygenLicenseVerifier { // Check for floating license context.isFloatingLicense = attributesObj.optBoolean("floating", false); context.maxMachines = attributesObj.optInt("maxMachines", 1); - + // Extract metadata JSONObject metadataObj = attributesObj.optJSONObject("metadata"); if (metadataObj != null) { @@ -411,14 +412,16 @@ public class KeygenLicenseVerifier { // Check for floating license in policy boolean policyFloating = policyObj.optBoolean("floating", false); int policyMaxMachines = policyObj.optInt("maxMachines", 1); - + // Policy settings take precedence if (policyFloating) { context.isFloatingLicense = true; context.maxMachines = policyMaxMachines; - log.info("Policy defines floating license with max machines: {}", context.maxMachines); + log.info( + "Policy defines floating license with max machines: {}", + context.maxMachines); } - + // Extract max users and isEnterprise from policy or metadata int users = policyObj.optInt("users", 1); context.isEnterpriseLicense = policyObj.optBoolean("isEnterprise", false); @@ -474,7 +477,8 @@ public class KeygenLicenseVerifier { activateMachine(licenseKey, licenseId, machineFingerprint, context); if (activated) { // Revalidate after activation - validationResponse = validateLicense(licenseKey, machineFingerprint, context); + validationResponse = + validateLicense(licenseKey, machineFingerprint, context); isValid = validationResponse != null && validationResponse @@ -494,8 +498,8 @@ public class KeygenLicenseVerifier { } } - private JsonNode validateLicense(String licenseKey, String machineFingerprint, LicenseContext context) - throws Exception { + private JsonNode validateLicense( + String licenseKey, String machineFingerprint, LicenseContext context) throws Exception { String requestBody = String.format( "{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}", @@ -514,7 +518,8 @@ public class KeygenLicenseVerifier { .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); log.info("ValidateLicenseResponse body: {}", response.body()); JsonNode jsonResponse = objectMapper.readTree(response.body()); if (response.statusCode() == 200) { @@ -527,21 +532,23 @@ public class KeygenLicenseVerifier { log.info("License validity: " + isValid); log.info("Validation detail: " + detail); log.info("Validation code: " + code); - + // Check if the license itself has floating attribute JsonNode licenseAttrs = jsonResponse.path("data").path("attributes"); if (!licenseAttrs.isMissingNode()) { context.isFloatingLicense = licenseAttrs.path("floating").asBoolean(false); context.maxMachines = licenseAttrs.path("maxMachines").asInt(1); - - log.info("License floating (from license): {}, maxMachines: {}", - context.isFloatingLicense, context.maxMachines); + + log.info( + "License floating (from license): {}, maxMachines: {}", + context.isFloatingLicense, + context.maxMachines); } - + // Also check the policy for floating license support if included JsonNode includedNode = jsonResponse.path("included"); JsonNode policyNode = null; - + if (includedNode.isArray()) { for (JsonNode node : includedNode) { if ("policies".equals(node.path("type").asText())) { @@ -550,20 +557,23 @@ public class KeygenLicenseVerifier { } } } - + if (policyNode != null) { // Check if this is a floating license from policy - boolean policyFloating = policyNode.path("attributes").path("floating").asBoolean(false); + boolean policyFloating = + policyNode.path("attributes").path("floating").asBoolean(false); int policyMaxMachines = policyNode.path("attributes").path("maxMachines").asInt(1); - + // Policy takes precedence over license attributes if (policyFloating) { context.isFloatingLicense = true; context.maxMachines = policyMaxMachines; } - - log.info("License floating (from policy): {}, maxMachines: {}", - context.isFloatingLicense, context.maxMachines); + + log.info( + "License floating (from policy): {}, maxMachines: {}", + context.isFloatingLicense, + context.maxMachines); } // Extract user count, default to 1 if not specified @@ -593,86 +603,104 @@ public class KeygenLicenseVerifier { return jsonResponse; } - private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint, - LicenseContext context) throws Exception { + private boolean activateMachine( + String licenseKey, String licenseId, String machineFingerprint, LicenseContext context) + throws Exception { // For floating licenses, we first need to check if we need to deregister any machines if (context.isFloatingLicense) { - log.info("Processing floating license activation. Max machines allowed: {}", context.maxMachines); - + log.info( + "Processing floating license activation. Max machines allowed: {}", + context.maxMachines); + // Get the current machines for this license JsonNode machinesResponse = fetchMachinesForLicense(licenseKey, licenseId); if (machinesResponse != null) { JsonNode machines = machinesResponse.path("data"); int currentMachines = machines.size(); - - log.info("Current machine count: {}, Max allowed: {}", currentMachines, context.maxMachines); - + + log.info( + "Current machine count: {}, Max allowed: {}", + currentMachines, + context.maxMachines); + // Check if the current fingerprint is already activated boolean isCurrentMachineActivated = false; String currentMachineId = null; - + for (JsonNode machine : machines) { - if (machineFingerprint.equals(machine.path("attributes").path("fingerprint").asText())) { + if (machineFingerprint.equals( + machine.path("attributes").path("fingerprint").asText())) { isCurrentMachineActivated = true; currentMachineId = machine.path("id").asText(); - log.info("Current machine is already activated with ID: {}", currentMachineId); + log.info( + "Current machine is already activated with ID: {}", + currentMachineId); break; } } - + // If the current machine is already activated, there's no need to do anything if (isCurrentMachineActivated) { log.info("Machine already activated. No action needed."); return true; } - + // If we've reached the max machines limit, we need to deregister the oldest machine if (currentMachines >= context.maxMachines) { - log.info("Max machines reached. Deregistering oldest machine to make room for the new machine."); - + log.info( + "Max machines reached. Deregistering oldest machine to make room for the new machine."); + // Find the oldest machine based on creation timestamp if (machines.size() > 0) { // Find the machine with the oldest creation date String oldestMachineId = null; java.time.Instant oldestTime = null; - + for (JsonNode machine : machines) { - String createdStr = machine.path("attributes").path("created").asText(null); + String createdStr = + machine.path("attributes").path("created").asText(null); if (createdStr != null && !createdStr.isEmpty()) { try { - java.time.Instant createdTime = java.time.Instant.parse(createdStr); + java.time.Instant createdTime = + java.time.Instant.parse(createdStr); if (oldestTime == null || createdTime.isBefore(oldestTime)) { oldestTime = createdTime; oldestMachineId = machine.path("id").asText(); } } catch (Exception e) { - log.warn("Could not parse creation time for machine: {}", e.getMessage()); + log.warn( + "Could not parse creation time for machine: {}", + e.getMessage()); } } } - + // If we couldn't determine the oldest by timestamp, use the first one if (oldestMachineId == null) { - log.warn("Could not determine oldest machine by timestamp, using first machine in list"); + log.warn( + "Could not determine oldest machine by timestamp, using first machine in list"); oldestMachineId = machines.path(0).path("id").asText(); } - + log.info("Deregistering machine with ID: {}", oldestMachineId); - + boolean deregistered = deregisterMachine(licenseKey, oldestMachineId); if (!deregistered) { - log.error("Failed to deregister machine. Cannot proceed with activation."); + log.error( + "Failed to deregister machine. Cannot proceed with activation."); return false; } - log.info("Machine deregistered successfully. Proceeding with activation of new machine."); + log.info( + "Machine deregistered successfully. Proceeding with activation of new machine."); } else { - log.error("License has reached machine limit but no machines were found to deregister. This is unexpected."); + log.error( + "License has reached machine limit but no machines were found to deregister. This is unexpected."); // We'll still try to activate, but it might fail } } } } - + // Proceed with machine activation String hostname; try { @@ -720,7 +748,8 @@ public class KeygenLicenseVerifier { .POST(HttpRequest.BodyPublishers.ofString(body.toString())) .build(); - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); log.info("activateMachine Response body: " + response.body()); if (response.statusCode() == 201) { log.info("Machine activated successfully"); @@ -738,61 +767,76 @@ public class KeygenLicenseVerifier { private String generateMachineFingerprint() { return GeneralUtils.generateMachineFingerprint(); } - + /** * Fetches all machines associated with a specific license - * + * * @param licenseKey The license key to check - * @param licenseId The license ID + * @param licenseId The license ID * @return JsonNode containing the list of machines, or null if an error occurs * @throws Exception if an error occurs during the HTTP request */ private JsonNode fetchMachinesForLicense(String licenseKey, String licenseId) throws Exception { - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/licenses/" + licenseId + "/machines")) - .header("Content-Type", "application/vnd.api+json") - .header("Accept", "application/vnd.api+json") - .header("Authorization", "License " + licenseKey) - .GET() - .build(); - - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + HttpRequest request = + HttpRequest.newBuilder() + .uri( + URI.create( + BASE_URL + + "/" + + ACCOUNT_ID + + "/licenses/" + + licenseId + + "/machines")) + .header("Content-Type", "application/vnd.api+json") + .header("Accept", "application/vnd.api+json") + .header("Authorization", "License " + licenseKey) + .GET() + .build(); + + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); log.info("fetchMachinesForLicense Response body: {}", response.body()); - + if (response.statusCode() == 200) { return objectMapper.readTree(response.body()); } else { - log.error("Error fetching machines for license. Status code: {}, error: {}", - response.statusCode(), response.body()); + log.error( + "Error fetching machines for license. Status code: {}, error: {}", + response.statusCode(), + response.body()); return null; } } - + /** * Deregisters a machine from a license - * + * * @param licenseKey The license key * @param machineId The ID of the machine to deregister * @return true if deregistration was successful, false otherwise */ private boolean deregisterMachine(String licenseKey, String machineId) { try { - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines/" + machineId)) - .header("Content-Type", "application/vnd.api+json") - .header("Accept", "application/vnd.api+json") - .header("Authorization", "License " + licenseKey) - .DELETE() - .build(); - - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - + HttpRequest request = + HttpRequest.newBuilder() + .uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines/" + machineId)) + .header("Content-Type", "application/vnd.api+json") + .header("Accept", "application/vnd.api+json") + .header("Authorization", "License " + licenseKey) + .DELETE() + .build(); + + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() == 204) { log.info("Machine {} successfully deregistered", machineId); return true; } else { - log.error("Error deregistering machine. Status code: {}, error: {}", - response.statusCode(), response.body()); + log.error( + "Error deregistering machine. Status code: {}, error: {}", + response.statusCode(), + response.body()); return false; } } catch (Exception e) { diff --git a/src/test/java/stirling/software/SPDF/service/CustomPDFDocumentFactoryTest.java b/src/test/java/stirling/software/SPDF/service/CustomPDFDocumentFactoryTest.java index 035011008..7fe84416b 100644 --- a/src/test/java/stirling/software/SPDF/service/CustomPDFDocumentFactoryTest.java +++ b/src/test/java/stirling/software/SPDF/service/CustomPDFDocumentFactoryTest.java @@ -1,18 +1,17 @@ package stirling.software.SPDF.service; -import java.nio.file.Files; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.io.*; import java.nio.file.*; +import java.nio.file.Files; import java.util.Arrays; import org.apache.pdfbox.Loader; +import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.common.PDStream; -import org.aspectj.lang.annotation.Before; -import org.apache.pdfbox.cos.COSName; import org.junit.jupiter.api.*; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -25,7 +24,7 @@ import stirling.software.SPDF.service.SpyPDFDocumentFactory.StrategyType; @TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@Execution(value = ExecutionMode.SAME_THREAD) +@Execution(value = ExecutionMode.SAME_THREAD) class CustomPDFDocumentFactoryTest { private SpyPDFDocumentFactory factory; @@ -43,12 +42,7 @@ class CustomPDFDocumentFactoryTest { } @ParameterizedTest - @CsvSource({ - "5,MEMORY_ONLY", - "20,MIXED", - "60,TEMP_FILE" - - }) + @CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"}) void testStrategy_FileInput(int sizeMB, StrategyType expected) throws IOException { File file = writeTempFile(inflatePdf(basePdfBytes, sizeMB)); try (PDDocument doc = factory.load(file)) { @@ -57,12 +51,7 @@ class CustomPDFDocumentFactoryTest { } @ParameterizedTest - @CsvSource({ - "5,MEMORY_ONLY", - "20,MIXED", - "60,TEMP_FILE" - - }) + @CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"}) void testStrategy_ByteArray(int sizeMB, StrategyType expected) throws IOException { byte[] inflated = inflatePdf(basePdfBytes, sizeMB); try (PDDocument doc = factory.load(inflated)) { @@ -71,12 +60,7 @@ class CustomPDFDocumentFactoryTest { } @ParameterizedTest - @CsvSource({ - "5,MEMORY_ONLY", - "20,MIXED", - "60,TEMP_FILE" - - }) + @CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"}) void testStrategy_InputStream(int sizeMB, StrategyType expected) throws IOException { byte[] inflated = inflatePdf(basePdfBytes, sizeMB); try (PDDocument doc = factory.load(new ByteArrayInputStream(inflated))) { @@ -85,30 +69,22 @@ class CustomPDFDocumentFactoryTest { } @ParameterizedTest - @CsvSource({ - "5,MEMORY_ONLY", - "20,MIXED", - "60,TEMP_FILE" - - }) + @CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"}) void testStrategy_MultipartFile(int sizeMB, StrategyType expected) throws IOException { byte[] inflated = inflatePdf(basePdfBytes, sizeMB); - MockMultipartFile multipart = new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); + MockMultipartFile multipart = + new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); try (PDDocument doc = factory.load(multipart)) { assertEquals(expected, factory.lastStrategyUsed); } } @ParameterizedTest - @CsvSource({ - "5,MEMORY_ONLY", - "20,MIXED", - "60,TEMP_FILE" - - }) + @CsvSource({"5,MEMORY_ONLY", "20,MIXED", "60,TEMP_FILE"}) void testStrategy_PDFFile(int sizeMB, StrategyType expected) throws IOException { byte[] inflated = inflatePdf(basePdfBytes, sizeMB); - MockMultipartFile multipart = new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); + MockMultipartFile multipart = + new MockMultipartFile("file", "doc.pdf", "application/pdf", inflated); PDFFile pdfFile = new PDFFile(); pdfFile.setFileInput(multipart); try (PDDocument doc = factory.load(pdfFile)) { @@ -125,14 +101,16 @@ class CustomPDFDocumentFactoryTest { stream.getCOSObject().setItem(COSName.TYPE, COSName.XOBJECT); stream.getCOSObject().setItem(COSName.SUBTYPE, COSName.IMAGE); - doc.getDocumentCatalog().getCOSObject().setItem(COSName.getPDFName("DummyBigStream"), stream.getCOSObject()); + doc.getDocumentCatalog() + .getCOSObject() + .setItem(COSName.getPDFName("DummyBigStream"), stream.getCOSObject()); ByteArrayOutputStream out = new ByteArrayOutputStream(); doc.save(out); return out.toByteArray(); } } - + @Test void testLoadFromPath() throws IOException { File file = writeTempFile(inflatePdf(basePdfBytes, 5)); @@ -151,29 +129,29 @@ class CustomPDFDocumentFactoryTest { } // neeed to add password pdf -// @Test -// void testLoadPasswordProtectedPdfFromInputStream() throws IOException { -// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) { -// assertNotNull(is, "protected.pdf must be present in src/test/resources"); -// try (PDDocument doc = factory.load(is, "test123")) { -// assertNotNull(doc); -// } -// } -// } -// -// @Test -// void testLoadPasswordProtectedPdfFromMultipart() throws IOException { -// try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) { -// assertNotNull(is, "protected.pdf must be present in src/test/resources"); -// byte[] bytes = is.readAllBytes(); -// MockMultipartFile file = new MockMultipartFile("file", "protected.pdf", "application/pdf", bytes); -// try (PDDocument doc = factory.load(file, "test123")) { -// assertNotNull(doc); -// } -// } -// } + // @Test + // void testLoadPasswordProtectedPdfFromInputStream() throws IOException { + // try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) { + // assertNotNull(is, "protected.pdf must be present in src/test/resources"); + // try (PDDocument doc = factory.load(is, "test123")) { + // assertNotNull(doc); + // } + // } + // } + // + // @Test + // void testLoadPasswordProtectedPdfFromMultipart() throws IOException { + // try (InputStream is = getClass().getResourceAsStream("/protected.pdf")) { + // assertNotNull(is, "protected.pdf must be present in src/test/resources"); + // byte[] bytes = is.readAllBytes(); + // MockMultipartFile file = new MockMultipartFile("file", "protected.pdf", + // "application/pdf", bytes); + // try (PDDocument doc = factory.load(file, "test123")) { + // assertNotNull(doc); + // } + // } + // } - @Test void testLoadReadOnlySkipsPostProcessing() throws IOException { PdfMetadataService mockService = mock(PdfMetadataService.class); @@ -186,7 +164,6 @@ class CustomPDFDocumentFactoryTest { } } - @Test void testCreateNewDocument() throws IOException { try (PDDocument doc = factory.createNewDocument()) { @@ -198,7 +175,7 @@ class CustomPDFDocumentFactoryTest { void testCreateNewDocumentBasedOnOldDocument() throws IOException { byte[] inflated = inflatePdf(basePdfBytes, 5); try (PDDocument oldDoc = Loader.loadPDF(inflated); - PDDocument newDoc = factory.createNewDocumentBasedOnOldDocument(oldDoc)) { + PDDocument newDoc = factory.createNewDocumentBasedOnOldDocument(oldDoc)) { assertNotNull(newDoc); } } @@ -241,7 +218,6 @@ class CustomPDFDocumentFactoryTest { @BeforeEach void cleanup() { - System.gc(); + System.gc(); } - } diff --git a/src/test/java/stirling/software/SPDF/service/SpyPDFDocumentFactory.java b/src/test/java/stirling/software/SPDF/service/SpyPDFDocumentFactory.java index ff53246d6..c7035c17d 100644 --- a/src/test/java/stirling/software/SPDF/service/SpyPDFDocumentFactory.java +++ b/src/test/java/stirling/software/SPDF/service/SpyPDFDocumentFactory.java @@ -1,14 +1,14 @@ package stirling.software.SPDF.service; + import org.apache.pdfbox.io.RandomAccessStreamCache.StreamCacheCreateFunction; -import stirling.software.SPDF.service.CustomPDFDocumentFactory; -import stirling.software.SPDF.service.PdfMetadataService; - class SpyPDFDocumentFactory extends CustomPDFDocumentFactory { - enum StrategyType { - MEMORY_ONLY, MIXED, TEMP_FILE - } - + enum StrategyType { + MEMORY_ONLY, + MIXED, + TEMP_FILE + } + public StrategyType lastStrategyUsed; public SpyPDFDocumentFactory(PdfMetadataService service) { @@ -28,4 +28,4 @@ class SpyPDFDocumentFactory extends CustomPDFDocumentFactory { this.lastStrategyUsed = type; return super.getStreamCacheFunction(contentSize); // delegate to real behavior } -} \ No newline at end of file +} diff --git a/src/test/java/stirling/software/SPDF/utils/CustomHtmlSanitizerTest.java b/src/test/java/stirling/software/SPDF/utils/CustomHtmlSanitizerTest.java index fc79db566..978970270 100644 --- a/src/test/java/stirling/software/SPDF/utils/CustomHtmlSanitizerTest.java +++ b/src/test/java/stirling/software/SPDF/utils/CustomHtmlSanitizerTest.java @@ -27,25 +27,28 @@ class CustomHtmlSanitizerTest { private static Stream provideHtmlTestCases() { return Stream.of( - Arguments.of( - "

This is valid HTML with formatting.

", - new String[] {"

", "", ""} - ), - Arguments.of( - "

Text with bold, italic, underline, " - + "emphasis, strong, strikethrough, " - + "strike, subscript, superscript, " - + "teletype, code, big, small.

", - new String[] {"bold", "italic", "emphasis", "strong"} - ), - Arguments.of( - "
Division

Heading 1

Heading 2

Heading 3

" - + "

Heading 4

Heading 5
Heading 6
" - + "
Blockquote
  • List item
" - + "
  1. Ordered item
", - new String[] {"
", "

", "

", "
", "
    ", "
      ", "
    1. "} - ) - ); + Arguments.of( + "

      This is valid HTML with formatting.

      ", + new String[] {"

      ", "", ""}), + Arguments.of( + "

      Text with bold, italic, underline, " + + "emphasis, strong, strikethrough, " + + "strike, subscript, superscript, " + + "teletype, code, big, small.

      ", + new String[] { + "bold", + "italic", + "emphasis", + "strong" + }), + Arguments.of( + "
      Division

      Heading 1

      Heading 2

      Heading 3

      " + + "

      Heading 4

      Heading 5
      Heading 6
      " + + "
      Blockquote
      • List item
      " + + "
      1. Ordered item
      ", + new String[] { + "
      ", "

      ", "

      ", "
      ", "
        ", "
          ", "
        1. " + })); } @Test diff --git a/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java b/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java index 7960128df..38b5e9277 100644 --- a/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java +++ b/src/test/java/stirling/software/SPDF/utils/PDFToFileTest.java @@ -1,6 +1,5 @@ package stirling.software.SPDF.utils; -import io.github.pixee.security.ZipSecurity; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,6 +28,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; +import io.github.pixee.security.ZipSecurity; + import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; /** @@ -214,7 +215,8 @@ class PDFToFileTest { // Verify the content by unzipping it try (ZipInputStream zipStream = - ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) { + ZipSecurity.createHardenedInputStream( + new java.io.ByteArrayInputStream(response.getBody()))) { ZipEntry entry; boolean foundMdFiles = false; boolean foundImage = false; @@ -286,7 +288,8 @@ class PDFToFileTest { // Verify the content by unzipping it try (ZipInputStream zipStream = - ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) { + ZipSecurity.createHardenedInputStream( + new java.io.ByteArrayInputStream(response.getBody()))) { ZipEntry entry; boolean foundMainHtml = false; boolean foundIndexHtml = false; @@ -437,7 +440,8 @@ class PDFToFileTest { // Verify the content by unzipping it try (ZipInputStream zipStream = - ZipSecurity.createHardenedInputStream(new java.io.ByteArrayInputStream(response.getBody()))) { + ZipSecurity.createHardenedInputStream( + new java.io.ByteArrayInputStream(response.getBody()))) { ZipEntry entry; boolean foundMainFile = false; boolean foundMediaFiles = false;