resolve merge conflicts in CustomHtmlSanitizerTest as well as FileToPdfTes

This commit is contained in:
Balázs Szücs 2025-07-28 20:30:13 +02:00
parent 5d831a7a5c
commit 7a182f9315
2 changed files with 284 additions and 373 deletions

View File

@ -1,21 +1,20 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import stirling.software.common.service.SsrfProtectionService;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@DisplayName("CustomHtmlSanitizer Tests")
class CustomHtmlSanitizerTest {
private CustomHtmlSanitizer customHtmlSanitizer;
@ -29,324 +28,240 @@ class CustomHtmlSanitizerTest {
// Allow all URLs by default for basic tests
when(mockSsrfProtectionService.isUrlAllowed(org.mockito.ArgumentMatchers.anyString())).thenReturn(true);
when(mockApplicationProperties.getSystem()).thenReturn(mockSystem);
when(mockSystem.getDisableSanitize()).thenReturn(false); // Enable sanitization for tests
when(mockSystem.getDisableSanitize()).thenReturn(false);
customHtmlSanitizer = new CustomHtmlSanitizer(mockSsrfProtectionService, mockApplicationProperties);
}
@ParameterizedTest
@MethodSource("provideHtmlTestCases")
void testSanitizeHtml(String inputHtml, String[] expectedContainedTags) {
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(inputHtml);
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
@DisplayName("Tag Preservation Tests")
class TagPreservationTests {
@ParameterizedTest(name = "should preserve tags: {1}")
@MethodSource("provideHtmlTestCases")
void testSanitizeHtml(String inputHtml, String[] expectedContainedTags) {
String sanitizedHtml = customHtmlSanitizer.sanitize(inputHtml);
for (String tag : expectedContainedTags) {
assertTrue(sanitizedHtml.contains(tag), tag + " should be preserved");
}
}
// Assert
for (String tag : expectedContainedTags) {
assertTrue(sanitizedHtml.contains(tag), tag + " should be preserved");
private Stream<Arguments> provideHtmlTestCases() {
return Stream.of(
Arguments.of(
"<p>This is <strong>valid</strong> HTML with <em>formatting</em>.</p>",
new String[]{"<p>", "<strong>", "<em>"}),
Arguments.of(
"<p>Text with <b>bold</b>, <i>italic</i>, <u>underline</u>, "
+ "<em>emphasis</em>, <strong>strong</strong>, <strike>strikethrough</strike>, "
+ "<s>strike</s>, <sub>subscript</sub>, <sup>superscript</sup>, "
+ "<tt>teletype</tt>, <code>code</code>, <big>big</big>, <small>small</small>.</p>",
new String[]{
"<b>bold</b>",
"<i>italic</i>",
"<em>emphasis</em>",
"<strong>strong</strong>"
}),
Arguments.of(
"<div>Division</div><h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3>"
+ "<h4>Heading 4</h4><h5>Heading 5</h5><h6>Heading 6</h6>"
+ "<blockquote>Blockquote</blockquote><ul><li>List item</li></ul>"
+ "<ol><li>Ordered item</li></ol>",
new String[]{
"<div>", "<h1>", "<h6>", "<blockquote>", "<ul>", "<ol>", "<li>"
}));
}
@Test
@DisplayName("Allows style attributes")
void testSanitizeAllowsStyles() {
String htmlWithStyles = "<p style=\"color: blue; font-size: 16px; margin-top: 10px;\">Styled text</p>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithStyles);
assertTrue(sanitizedHtml.contains("<p"), "Paragraph tag should be preserved");
assertTrue(sanitizedHtml.contains("style="), "Style attribute should be preserved");
assertTrue(sanitizedHtml.contains("Styled text"), "Content should be preserved");
}
@Test
@DisplayName("Allows safe links")
void testSanitizeAllowsLinks() {
String htmlWithLink = "<a href=\"https://example.com\" title=\"Example Site\">Example Link</a>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithLink);
assertTrue(sanitizedHtml.contains("Example Link"), "Link text should be preserved");
assertTrue(sanitizedHtml.contains("href="), "Link href attribute should be present");
assertTrue(sanitizedHtml.contains("example.com"), "Link URL should be preserved");
}
@Test
@DisplayName("Allows tables")
void testSanitizeAllowsTables() {
String htmlWithTable =
"<table border=\"1\">"
+ "<thead><tr><th>Header 1</th><th>Header 2</th></tr></thead>"
+ "<tbody><tr><td>Cell 1</td><td>Cell 2</td></tr></tbody>"
+ "<tfoot><tr><td colspan=\"2\">Footer</td></tr></tfoot>"
+ "</table>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithTable);
assertTrue(sanitizedHtml.contains("<table"), "Table should be preserved");
assertTrue(sanitizedHtml.contains("<tr>"), "Table rows should be preserved");
assertTrue(sanitizedHtml.contains("<th>"), "Table headers should be preserved");
assertTrue(sanitizedHtml.contains("<td>"), "Table cells should be preserved");
assertTrue(sanitizedHtml.contains("Header 1"), "Table header content should be preserved");
assertTrue(sanitizedHtml.contains("Cell 1"), "Table cell content should be preserved");
assertTrue(sanitizedHtml.contains("Footer"), "Table footer content should be preserved");
}
@Test
@DisplayName("Allows images")
void testSanitizeAllowsImages() {
String htmlWithImage = "<img src=\"image.jpg\" alt=\"An image\" width=\"100\" height=\"100\">";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithImage);
assertTrue(sanitizedHtml.contains("<img"), "Image tag should be preserved");
assertTrue(sanitizedHtml.contains("src=\"image.jpg\""), "Image source should be preserved");
assertTrue(sanitizedHtml.contains("alt=\"An image\""), "Image alt text should be preserved");
}
}
private static Stream<Arguments> provideHtmlTestCases() {
return Stream.of(
Arguments.of(
"<p>This is <strong>valid</strong> HTML with <em>formatting</em>.</p>",
new String[] {"<p>", "<strong>", "<em>"}),
Arguments.of(
"<p>Text with <b>bold</b>, <i>italic</i>, <u>underline</u>, "
+ "<em>emphasis</em>, <strong>strong</strong>, <strike>strikethrough</strike>, "
+ "<s>strike</s>, <sub>subscript</sub>, <sup>superscript</sup>, "
+ "<tt>teletype</tt>, <code>code</code>, <big>big</big>, <small>small</small>.</p>",
new String[] {
"<b>bold</b>",
"<i>italic</i>",
"<em>emphasis</em>",
"<strong>strong</strong>"
}),
Arguments.of(
"<div>Division</div><h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3>"
+ "<h4>Heading 4</h4><h5>Heading 5</h5><h6>Heading 6</h6>"
+ "<blockquote>Blockquote</blockquote><ul><li>List item</li></ul>"
+ "<ol><li>Ordered item</li></ol>",
new String[] {
"<div>", "<h1>", "<h6>", "<blockquote>", "<ul>", "<ol>", "<li>"
}));
}
@Nested
@DisplayName("Tag/Attribute Removal and XSS Tests")
class TagAndXssPreventionTests {
@Test
@DisplayName("Removes JavaScript URLs from href")
void testSanitizeDisallowsJavaScriptLinks() {
String htmlWithJsLink = "<a href=\"javascript:alert('XSS')\">Malicious Link</a>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithJsLink);
assertFalse(sanitizedHtml.contains("javascript:"), "JavaScript URLs should be removed");
assertTrue(sanitizedHtml.contains("Malicious Link"), "Link text should be preserved");
}
@Test
void testSanitizeAllowsStyles() {
// Arrange - Testing Sanitizers.STYLES
String htmlWithStyles =
"<p style=\"color: blue; font-size: 16px; margin-top: 10px;\">Styled text</p>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithStyles);
// Assert
// The OWASP HTML Sanitizer might filter some specific styles, so we only check that
// the sanitized HTML is not empty and contains a paragraph tag with style
assertTrue(sanitizedHtml.contains("<p"), "Paragraph tag should be preserved");
assertTrue(sanitizedHtml.contains("style="), "Style attribute should be preserved");
assertTrue(sanitizedHtml.contains("Styled text"), "Content should be preserved");
}
@Test
void testSanitizeAllowsLinks() {
// Arrange - Testing Sanitizers.LINKS
String htmlWithLink =
"<a href=\"https://example.com\" title=\"Example Site\">Example Link</a>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithLink);
// Assert
// The most important aspect is that the link content is preserved
assertTrue(sanitizedHtml.contains("Example Link"), "Link text should be preserved");
// Check that the href is present in some form
assertTrue(sanitizedHtml.contains("href="), "Link href attribute should be present");
// Check that the URL is present in some form
assertTrue(sanitizedHtml.contains("example.com"), "Link URL should be preserved");
// OWASP sanitizer may handle title attributes differently depending on version
// So we won't make strict assertions about the title attribute
}
@Test
void testSanitizeDisallowsJavaScriptLinks() {
// Arrange
String htmlWithJsLink = "<a href=\"javascript:alert('XSS')\">Malicious Link</a>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithJsLink);
// Assert
assertFalse(sanitizedHtml.contains("javascript:"), "JavaScript URLs should be removed");
// The link tag might still be there, but the href should be sanitized
assertTrue(sanitizedHtml.contains("Malicious Link"), "Link text should be preserved");
}
@Test
void testSanitizeAllowsTables() {
// Arrange - Testing Sanitizers.TABLES
String htmlWithTable =
"<table border=\"1\">"
+ "<thead><tr><th>Header 1</th><th>Header 2</th></tr></thead>"
+ "<tbody><tr><td>Cell 1</td><td>Cell 2</td></tr></tbody>"
+ "<tfoot><tr><td colspan=\"2\">Footer</td></tr></tfoot>"
+ "</table>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithTable);
// Assert
assertTrue(sanitizedHtml.contains("<table"), "Table should be preserved");
assertTrue(sanitizedHtml.contains("<tr>"), "Table rows should be preserved");
assertTrue(sanitizedHtml.contains("<th>"), "Table headers should be preserved");
assertTrue(sanitizedHtml.contains("<td>"), "Table cells should be preserved");
// Note: border attribute might be removed as it's deprecated in HTML5
// Check for content values instead of exact tag formats because
// the sanitizer may normalize tags and attributes
assertTrue(sanitizedHtml.contains("Header 1"), "Table header content should be preserved");
assertTrue(sanitizedHtml.contains("Cell 1"), "Table cell content should be preserved");
assertTrue(sanitizedHtml.contains("Footer"), "Table footer content should be preserved");
// OWASP sanitizer may not preserve these structural elements or attributes in the same
// format
// So we check for the content rather than the exact structure
}
@Test
void testSanitizeAllowsImages() {
// Arrange - Testing Sanitizers.IMAGES
String htmlWithImage =
"<img src=\"image.jpg\" alt=\"An image\" width=\"100\" height=\"100\">";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithImage);
// Assert
assertTrue(sanitizedHtml.contains("<img"), "Image tag should be preserved");
assertTrue(sanitizedHtml.contains("src=\"image.jpg\""), "Image source should be preserved");
assertTrue(
sanitizedHtml.contains("alt=\"An image\""), "Image alt text should be preserved");
// Width and height might be preserved, but not guaranteed by all sanitizers
}
@Test
void testSanitizeDisallowsDataUrlImages() {
// Arrange
String htmlWithDataUrlImage =
"<img src=\"data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9ImFsZXJ0KDEpIj48L3N2Zz4=\" alt=\"SVG with XSS\">";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithDataUrlImage);
// Assert
assertFalse(
@Test
@DisplayName("Removes data URL images")
void testSanitizeDisallowsDataUrlImages() {
String htmlWithDataUrlImage = "<img src=\"data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9ImFsZXJ0KDEpIj48L3N2Zz4=\" alt=\"SVG with XSS\">";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithDataUrlImage);
assertFalse(
sanitizedHtml.contains("data:image/svg"),
"Data URLs with potentially malicious content should be removed");
}
}
@Test
void testSanitizeRemovesJavaScriptInAttributes() {
// Arrange
String htmlWithJsEvent =
"<a href=\"#\" onclick=\"alert('XSS')\" onmouseover=\"alert('XSS')\">Click me</a>";
@Test
@DisplayName("Removes JS event handler attributes")
void testSanitizeRemovesJavaScriptInAttributes() {
String htmlWithJsEvent = "<a href=\"#\" onclick=\"alert('XSS')\" onmouseover=\"alert('XSS')\">Click me</a>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithJsEvent);
assertFalse(sanitizedHtml.contains("onclick"), "JavaScript event handlers should be removed");
assertFalse(sanitizedHtml.contains("onmouseover"), "JavaScript event handlers should be removed");
assertTrue(sanitizedHtml.contains("Click me"), "Link text should be preserved");
}
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithJsEvent);
@Test
@DisplayName("Removes <script> tags")
void testSanitizeRemovesScriptTags() {
String htmlWithScript = "<p>Safe content</p><script>alert('XSS');</script>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithScript);
assertFalse(sanitizedHtml.contains("<script>"), "Script tags should be removed");
assertTrue(sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
// Assert
assertFalse(
sanitizedHtml.contains("onclick"), "JavaScript event handlers should be removed");
assertFalse(
sanitizedHtml.contains("onmouseover"),
"JavaScript event handlers should be removed");
assertTrue(sanitizedHtml.contains("Click me"), "Link text should be preserved");
}
@Test
@DisplayName("Removes <noscript> tags")
void testSanitizeRemovesNoScriptTags() {
String htmlWithNoscript = "<p>Safe content</p><noscript>JavaScript is disabled</noscript>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithNoscript);
assertFalse(sanitizedHtml.contains("<noscript>"), "Noscript tags should be removed");
assertTrue(sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
@Test
void testSanitizeRemovesScriptTags() {
// Arrange
String htmlWithScript = "<p>Safe content</p><script>alert('XSS');</script>";
@Test
@DisplayName("Removes <iframe> tags")
void testSanitizeRemovesIframes() {
String htmlWithIframe = "<p>Safe content</p><iframe src=\"https://example.com\"></iframe>";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithIframe);
assertFalse(sanitizedHtml.contains("<iframe"), "Iframe tags should be removed");
assertTrue(sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithScript);
// Assert
assertFalse(sanitizedHtml.contains("<script>"), "Script tags should be removed");
assertTrue(
sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
@Test
void testSanitizeRemovesNoScriptTags() {
// Arrange - Testing the custom policy to disallow noscript
String htmlWithNoscript = "<p>Safe content</p><noscript>JavaScript is disabled</noscript>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithNoscript);
// Assert
assertFalse(sanitizedHtml.contains("<noscript>"), "Noscript tags should be removed");
assertTrue(
sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
@Test
void testSanitizeRemovesIframes() {
// Arrange
String htmlWithIframe = "<p>Safe content</p><iframe src=\"https://example.com\"></iframe>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithIframe);
// Assert
assertFalse(sanitizedHtml.contains("<iframe"), "Iframe tags should be removed");
assertTrue(
sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
@Test
void testSanitizeRemovesObjectAndEmbed() {
// Arrange
String htmlWithObjects =
@Test
@DisplayName("Removes <object> and <embed> tags")
void testSanitizeRemovesObjectAndEmbed() {
String htmlWithObjects =
"<p>Safe content</p>"
+ "<object data=\"data.swf\" type=\"application/x-shockwave-flash\"></object>"
+ "<embed src=\"embed.swf\" type=\"application/x-shockwave-flash\">";
+ "<object data=\"data.swf\" type=\"application/x-shockwave-flash\"></object>"
+ "<embed src=\"embed.swf\" type=\"application/x-shockwave-flash\">";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithObjects);
assertFalse(sanitizedHtml.contains("<object"), "Object tags should be removed");
assertFalse(sanitizedHtml.contains("<embed"), "Embed tags should be removed");
assertTrue(sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithObjects);
// Assert
assertFalse(sanitizedHtml.contains("<object"), "Object tags should be removed");
assertFalse(sanitizedHtml.contains("<embed"), "Embed tags should be removed");
assertTrue(
sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
@Test
void testSanitizeRemovesMetaAndBaseAndLink() {
// Arrange
String htmlWithMetaTags =
@Test
@DisplayName("Removes <meta>, <base>, and <link> tags")
void testSanitizeRemovesMetaAndBaseAndLink() {
String htmlWithMetaTags =
"<p>Safe content</p>"
+ "<meta http-equiv=\"refresh\" content=\"0; url=http://evil.com\">"
+ "<base href=\"http://evil.com/\">"
+ "<link rel=\"stylesheet\" href=\"evil.css\">";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithMetaTags);
// Assert
assertFalse(sanitizedHtml.contains("<meta"), "Meta tags should be removed");
assertFalse(sanitizedHtml.contains("<base"), "Base tags should be removed");
assertFalse(sanitizedHtml.contains("<link"), "Link tags should be removed");
assertTrue(
sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
+ "<meta http-equiv=\"refresh\" content=\"0; url=http://evil.com\">"
+ "<base href=\"http://evil.com/\">"
+ "<link rel=\"stylesheet\" href=\"evil.css\">";
String sanitizedHtml = customHtmlSanitizer.sanitize(htmlWithMetaTags);
assertFalse(sanitizedHtml.contains("<meta"), "Meta tags should be removed");
assertFalse(sanitizedHtml.contains("<base"), "Base tags should be removed");
assertFalse(sanitizedHtml.contains("<link"), "Link tags should be removed");
assertTrue(sanitizedHtml.contains("<p>Safe content</p>"), "Safe content should be preserved");
}
}
@Test
void testSanitizeHandlesComplexHtml() {
// Arrange
String complexHtml =
@Nested
@DisplayName("Complex and Edge Case Tests")
class ComplexAndEdgeCaseTests {
@Test
@DisplayName("Handles complex HTML structures by preserving safe elements and removing unsafe ones")
void testSanitizeHandlesComplexHtml() {
String complexHtml =
"<div class=\"container\">"
+ " <h1 style=\"color: blue;\">Welcome</h1>"
+ " <p>This is a <strong>test</strong> with <a href=\"https://example.com\">link</a>.</p>"
+ " <table>"
+ " <tr><th>Name</th><th>Value</th></tr>"
+ " <tr><td>Item 1</td><td>100</td></tr>"
+ " </table>"
+ " <img src=\"image.jpg\" alt=\"Test image\">"
+ " <script>alert('XSS');</script>"
+ " <iframe src=\"https://evil.com\"></iframe>"
+ "</div>";
+ " <h1 style=\"color: blue;\">Welcome</h1>"
+ " <p>This is a <strong>test</strong> with <a href=\"https://example.com\">link</a>.</p>"
+ " <table>"
+ " <tr><th>Name</th><th>Value</th></tr>"
+ " <tr><td>Item 1</td><td>100</td></tr>"
+ " </table>"
+ " <img src=\"image.jpg\" alt=\"Test image\">"
+ " <script>alert('XSS');</script>"
+ " <iframe src=\"https://evil.com\"></iframe>"
+ "</div>";
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(complexHtml);
// Assert
assertTrue(sanitizedHtml.contains("<div"), "Div should be preserved");
assertTrue(sanitizedHtml.contains("<h1"), "H1 should be preserved");
assertTrue(
sanitizedHtml.contains("<strong>") && sanitizedHtml.contains("test"),
String sanitizedHtml = customHtmlSanitizer.sanitize(complexHtml);
assertTrue(sanitizedHtml.contains("<div"), "Div should be preserved");
assertTrue(sanitizedHtml.contains("<h1"), "H1 should be preserved");
assertTrue(sanitizedHtml.contains("<strong>") && sanitizedHtml.contains("test"),
"Strong tag should be preserved");
// Check for content rather than exact formatting
assertTrue(
sanitizedHtml.contains("<a")
&& sanitizedHtml.contains("href=")
&& sanitizedHtml.contains("example.com")
&& sanitizedHtml.contains("link"),
assertTrue(sanitizedHtml.contains("<a")
&& sanitizedHtml.contains("href=")
&& sanitizedHtml.contains("example.com")
&& sanitizedHtml.contains("link"),
"Link should be preserved");
assertTrue(sanitizedHtml.contains("<table"), "Table should be preserved");
assertTrue(sanitizedHtml.contains("<img"), "Image should be preserved");
assertFalse(sanitizedHtml.contains("<script>"), "Script tag should be removed");
assertFalse(sanitizedHtml.contains("<iframe"), "Iframe tag should be removed");
assertTrue(sanitizedHtml.contains("Welcome"), "Heading content should be preserved");
assertTrue(sanitizedHtml.contains("Name"), "Table header content should be preserved");
assertTrue(sanitizedHtml.contains("Item 1"), "Table data content should be preserved");
}
assertTrue(sanitizedHtml.contains("<table"), "Table should be preserved");
assertTrue(sanitizedHtml.contains("<img"), "Image should be preserved");
assertFalse(sanitizedHtml.contains("<script>"), "Script tag should be removed");
assertFalse(sanitizedHtml.contains("<iframe"), "Iframe tag should be removed");
@Test
@DisplayName("Returns empty string for empty input")
void testSanitizeHandlesEmpty() {
String sanitizedHtml = customHtmlSanitizer.sanitize("");
assertEquals("", sanitizedHtml, "Empty input should result in empty string");
}
// Content checks
assertTrue(sanitizedHtml.contains("Welcome"), "Heading content should be preserved");
assertTrue(sanitizedHtml.contains("Name"), "Table header content should be preserved");
assertTrue(sanitizedHtml.contains("Item 1"), "Table data content should be preserved");
}
@Test
void testSanitizeHandlesEmpty() {
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize("");
// Assert
assertEquals("", sanitizedHtml, "Empty input should result in empty string");
}
@Test
void testSanitizeHandlesNull() {
// Act
String sanitizedHtml = customHtmlSanitizer.sanitize(null);
// Assert
assertEquals("", sanitizedHtml, "Null input should result in empty string");
@Test
@DisplayName("Returns empty string for null input")
void testSanitizeHandlesNull() {
String sanitizedHtml = customHtmlSanitizer.sanitize(null);
assertEquals("", sanitizedHtml, "Null input should result in empty string");
}
}
}

View File

@ -1,23 +1,24 @@
package stirling.software.common.util;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.anyString;
import java.io.File;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
import stirling.software.common.service.SsrfProtectionService;
public class FileToPdfTest {
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@DisplayName("FileToPdf Tests")
class FileToPdfTest {
private CustomHtmlSanitizer customHtmlSanitizer;
@ -27,83 +28,78 @@ public class FileToPdfTest {
stirling.software.common.model.ApplicationProperties mockApplicationProperties = mock(stirling.software.common.model.ApplicationProperties.class);
stirling.software.common.model.ApplicationProperties.System mockSystem = mock(stirling.software.common.model.ApplicationProperties.System.class);
when(mockSsrfProtectionService.isUrlAllowed(org.mockito.ArgumentMatchers.anyString())).thenReturn(true);
when(mockSsrfProtectionService.isUrlAllowed(anyString())).thenReturn(true);
when(mockApplicationProperties.getSystem()).thenReturn(mockSystem);
when(mockSystem.getDisableSanitize()).thenReturn(false);
customHtmlSanitizer = new CustomHtmlSanitizer(mockSsrfProtectionService, mockApplicationProperties);
}
/**
* 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 (empty input)
String fileName = "test.html"; // Sample file name indicating an HTML file
TempFileManager tempFileManager = mock(TempFileManager.class); // Mock TempFileManager
@Nested
@DisplayName("HTML to PDF Conversion Tests")
class HtmlToPdfConversionTests {
@Test
@DisplayName("Throws exception on empty input")
void testConvertHtmlToPdf() {
HTMLToPdfRequest request = new HTMLToPdfRequest();
byte[] fileBytes = new byte[0]; // Sample file bytes (empty input)
String fileName = "test.html";
TempFileManager tempFileManager = mock(TempFileManager.class);
// Mock the temp file creation to return real temp files
try {
when(tempFileManager.createTempFile(anyString()))
.thenReturn(Files.createTempFile("test", ".pdf").toFile())
.thenReturn(Files.createTempFile("test", ".html").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
// Mock temp file creation
try {
when(tempFileManager.createTempFile(anyString()))
.thenReturn(Files.createTempFile("test", ".pdf").toFile())
.thenReturn(Files.createTempFile("test", ".html").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
Throwable thrown = assertThrows(
Exception.class,
() -> FileToPdf.convertHtmlToPdf("/path/", request, fileBytes, fileName, tempFileManager, customHtmlSanitizer),
"Should throw exception for empty input or invalid environment"
);
assertNotNull(thrown, "Exception should not be null");
}
}
@Nested
@DisplayName("ZIP Filename Sanitization Tests")
class ZipFilenameSanitizationTests {
@Test
@DisplayName("Returns empty string for null or empty input")
void testSanitizeZipFilename_NullOrEmpty() {
assertEquals("", FileToPdf.sanitizeZipFilename(null), "Null input should result in empty string");
assertEquals("", FileToPdf.sanitizeZipFilename(" "), "Blank input should result in empty string");
}
// Expect an IOException to be thrown due to empty input or invalid weasyprint path
Throwable thrown =
assertThrows(
Exception.class,
() ->
FileToPdf.convertHtmlToPdf(
"/path/", request, fileBytes, fileName, tempFileManager, customHtmlSanitizer));
assertNotNull(thrown);
}
@Test
@DisplayName("Removes path traversal sequences and normalizes separators")
void testSanitizeZipFilename_RemovesTraversalSequences() {
String input = "../some/../path/..\\to\\file.txt";
String expected = "some/path/to/file.txt";
assertEquals(expected, FileToPdf.sanitizeZipFilename(input), "Traversal sequences should be removed");
}
/**
* Test sanitizeZipFilename with null or empty input. It should return an empty string in these
* cases.
*/
@Test
public void testSanitizeZipFilename_NullOrEmpty() {
assertEquals("", FileToPdf.sanitizeZipFilename(null));
assertEquals("", FileToPdf.sanitizeZipFilename(" "));
}
@Test
@DisplayName("Removes leading drive letters and slashes")
void testSanitizeZipFilename_RemovesLeadingDriveAndSlashes() {
String input = "C:\\folder\\file.txt";
String expected = "folder/file.txt";
assertEquals(expected, FileToPdf.sanitizeZipFilename(input), "Leading drive letters should be removed");
/**
* 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";
input = "/folder/file.txt";
expected = "folder/file.txt";
assertEquals(expected, FileToPdf.sanitizeZipFilename(input), "Leading slash should be removed");
}
// 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));
@Test
@DisplayName("Leaves safe filenames unchanged")
void testSanitizeZipFilename_NoChangeForSafeNames() {
String input = "folder/subfolder/file.txt";
assertEquals(input, FileToPdf.sanitizeZipFilename(input), "Safe filename should be unchanged");
}
}
}