diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java
index c55597b47..557c77c76 100644
--- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java
+++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/OverlayImageController.java
@@ -25,6 +25,7 @@ import stirling.software.common.annotations.api.MiscApi;
import stirling.software.common.enumeration.ResourceWeight;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.GeneralUtils;
+import stirling.software.common.util.SvgSanitizer;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils;
@@ -36,6 +37,7 @@ public class OverlayImageController {
private final CustomPDFDocumentFactory pdfDocumentFactory;
private final TempFileManager tempFileManager;
+ private final SvgSanitizer svgSanitizer;
@AutoJobPostMapping(
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
@@ -61,6 +63,9 @@ public class OverlayImageController {
byte[] imageBytes = imageFile.getBytes();
boolean isSvg = SvgOverlayUtil.isSvgImage(imageBytes);
+ if (isSvg) {
+ imageBytes = svgSanitizer.sanitize(imageBytes);
+ }
try (PDDocument document = pdfDocumentFactory.load(pdfBytes)) {
int pages = document.getNumberOfPages();
diff --git a/app/core/src/main/java/stirling/software/SPDF/utils/SvgOverlayUtil.java b/app/core/src/main/java/stirling/software/SPDF/utils/SvgOverlayUtil.java
index 253d9c687..b58bb055f 100644
--- a/app/core/src/main/java/stirling/software/SPDF/utils/SvgOverlayUtil.java
+++ b/app/core/src/main/java/stirling/software/SPDF/utils/SvgOverlayUtil.java
@@ -10,6 +10,7 @@ import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
@@ -39,7 +40,16 @@ public class SvgOverlayUtil {
svgDoc = factory.createSVGDocument("file:///overlay.svg", inputStream);
}
- UserAgent userAgent = new UserAgentAdapter();
+ UserAgent userAgent =
+ new UserAgentAdapter() {
+ @Override
+ public void checkLoadExternalResource(
+ ParsedURL resourceURL, ParsedURL docURL) {
+ throw new SecurityException(
+ "External resource loading is disabled for SVG overlays: "
+ + resourceURL);
+ }
+ };
DocumentLoader loader = new DocumentLoader(userAgent);
BridgeContext ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);
diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/OverlayImageControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/OverlayImageControllerTest.java
index 308518e8f..6a13fb304 100644
--- a/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/OverlayImageControllerTest.java
+++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/misc/OverlayImageControllerTest.java
@@ -32,6 +32,7 @@ import org.springframework.mock.web.MockMultipartFile;
import stirling.software.SPDF.model.api.misc.OverlayImageRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
+import stirling.software.common.util.SvgSanitizer;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils;
@@ -52,6 +53,7 @@ class OverlayImageControllerTest {
@Mock private CustomPDFDocumentFactory pdfDocumentFactory;
@Mock private TempFileManager tempFileManager;
+ @Mock private SvgSanitizer svgSanitizer;
@InjectMocks private OverlayImageController controller;
@@ -205,6 +207,52 @@ class OverlayImageControllerTest {
mockDoc.close();
}
+ @Test
+ void overlayImage_svgInput_sanitizedBeforeOverlay() throws Exception {
+ byte[] maliciousSvg =
+ ("")
+ .getBytes();
+ byte[] sanitized =
+ ("")
+ .getBytes();
+ when(svgSanitizer.sanitize(maliciousSvg)).thenReturn(sanitized);
+
+ MockMultipartFile svgFile =
+ new MockMultipartFile("imageFile", "overlay.svg", "image/svg+xml", maliciousSvg);
+ OverlayImageRequest request = new OverlayImageRequest();
+ request.setFileInput(pdfFile);
+ request.setImageFile(svgFile);
+ request.setX(0);
+ request.setY(0);
+ request.setEveryPage(false);
+
+ PDDocument mockDoc = new PDDocument();
+ mockDoc.addPage(new PDPage(PDRectangle.A4));
+ when(pdfDocumentFactory.load(any(byte[].class))).thenReturn(mockDoc);
+
+ try (MockedStatic mockedWebResponse =
+ mockStatic(WebResponseUtils.class)) {
+ mockedWebResponse
+ .when(
+ () ->
+ WebResponseUtils.pdfFileToWebResponse(
+ any(TempFile.class), anyString()))
+ .thenReturn(streamingOk("result".getBytes()));
+
+ controller.overlayImage(request);
+ }
+ mockDoc.close();
+
+ verify(svgSanitizer).sanitize(maliciousSvg);
+ }
+
@Test
void overlayImage_withCoordinates_usesXY() throws Exception {
OverlayImageRequest request = new OverlayImageRequest();