mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Minor: sanitize SVG (#6572)
# Description of Changes Sanitize SVG --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
This commit is contained in:
+5
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
+48
@@ -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 =
|
||||
("<svg xmlns=\"http://www.w3.org/2000/svg\""
|
||||
+ " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
|
||||
+ " width=\"10\" height=\"10\">"
|
||||
+ "<image x=\"0\" y=\"0\" width=\"10\" height=\"10\""
|
||||
+ " xlink:href=\"file:///etc/passwd\"/>"
|
||||
+ "</svg>")
|
||||
.getBytes();
|
||||
byte[] sanitized =
|
||||
("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\">"
|
||||
+ "<image x=\"0\" y=\"0\" width=\"10\" height=\"10\"/>"
|
||||
+ "</svg>")
|
||||
.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<WebResponseUtils> 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();
|
||||
|
||||
Reference in New Issue
Block a user