Skip to content

Commit 11f4e52

Browse files
committed
Fixed: Path traversal via productId in ImageManagementServices
The productId request parameter was concatenated directly into filesystem paths across 7 service methods in ImageManagementServices without any path traversal validation. Added Paths.normalize().startsWith() guards in all affected methods (addMultipleuploadForProduct, removeImageFileForImageManagement, scaleImageMangementInAllSize, createContentThumbnail, createNewImageThumbnail, resizeImageOfProduct, renameImage), following the same pattern already in use in ProductServices.java.
1 parent c5e7acc commit 11f4e52

1 file changed

Lines changed: 60 additions & 1 deletion

File tree

applications/product/src/main/java/org/apache/ofbiz/product/imagemanagement/ImageManagementServices.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.nio.ByteBuffer;
2929
import java.nio.file.Files;
3030
import java.nio.file.Path;
31+
import java.nio.file.Paths;
3132
import java.nio.file.StandardCopyOption;
3233
import java.nio.file.StandardOpenOption;
3334
import java.util.HashMap;
@@ -92,6 +93,14 @@ public static Map<String, Object> addMultipleuploadForProduct(DispatchContext dc
9293
"image.management.path", delegator), context);
9394
String imageServerUrl = FlexibleStringExpander.expandString(EntityUtilProperties.getPropertyValue("catalog",
9495
"image.management.url", delegator), context);
96+
// Guard against path traversal via productId
97+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
98+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
99+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
100+
Debug.logError("Path traversal attempt detected in image management upload, productId: " + productId, MODULE);
101+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
102+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale));
103+
}
95104
String rootTargetDirectory = imageServerPath;
96105
File rootTargetDir = new File(rootTargetDirectory);
97106
if (!rootTargetDir.exists()) {
@@ -306,11 +315,20 @@ public static Map<String, Object> removeImageFileForImageManagement(DispatchCont
306315
String contentId = (String) context.get("contentId");
307316
String dataResourceName = (String) context.get("dataResourceName");
308317
Delegator delegator = dctx.getDelegator();
318+
Locale locale = (Locale) context.get("locale");
309319

310320
try {
311321
if (UtilValidate.isNotEmpty(contentId)) {
312322
String imageServerPath = FlexibleStringExpander.expandString(EntityUtilProperties.getPropertyValue("catalog",
313323
"image.management.path", delegator), context);
324+
// Guard against path traversal via productId or dataResourceName
325+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
326+
Path resolvedFilePath = Paths.get(imageServerPath, productId, dataResourceName).normalize();
327+
if (!resolvedFilePath.startsWith(imageServerNormalizedPath)) {
328+
Debug.logError("Path traversal attempt detected in image management remove, productId: " + productId, MODULE);
329+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
330+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedFilePath.toString()), locale));
331+
}
314332
File file = new File(imageServerPath + "/" + productId + "/" + dataResourceName);
315333
if (!file.delete()) {
316334
Debug.logError("File :" + file.getName() + ", couldn't be deleted", MODULE);
@@ -369,7 +387,16 @@ private static Map<String, Object> scaleImageMangementInAllSize(DispatchContext
369387
"image.management.path", dctx.getDelegator()), context);
370388
String imageServerUrl = FlexibleStringExpander.expandString(EntityUtilProperties.getPropertyValue("catalog",
371389
"image.management.url", dctx.getDelegator()), context);
372-
390+
// Guard against path traversal via productId
391+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
392+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
393+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
394+
Debug.logError("Path traversal attempt detected in image management scale, productId: " + productId, MODULE);
395+
String errMsg = UtilProperties.getMessage(RES_ERROR,
396+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale);
397+
result.put(ModelService.ERROR_MESSAGE, errMsg);
398+
return result;
399+
}
373400

374401
/* get original BUFFERED IMAGE */
375402
resultBufImgMap.putAll(ImageTransform.getBufferedImage(imageServerPath + "/" + productId + "/" + filenameToUse, locale));
@@ -536,6 +563,14 @@ public static Map<String, Object> createContentThumbnail(DispatchContext dctx, M
536563
"image.management.path", delegator), context);
537564
String nameOfThumb = FlexibleStringExpander.expandString(EntityUtilProperties.getPropertyValue("catalog",
538565
"image.management.nameofthumbnail", delegator), context);
566+
// Guard against path traversal via productId
567+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
568+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
569+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
570+
Debug.logError("Path traversal attempt detected in image management thumbnail, productId: " + productId, MODULE);
571+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
572+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale));
573+
}
539574

540575
// Create content for thumbnail
541576
Map<String, Object> contentThumb = new HashMap<>();
@@ -729,6 +764,14 @@ public static Map<String, Object> createNewImageThumbnail(DispatchContext dctx,
729764
String contentId = (String) context.get("contentId");
730765
String dataResourceName = (String) context.get("dataResourceName");
731766
String width = (String) context.get("sizeWidth");
767+
// Guard against path traversal via productId
768+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
769+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
770+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
771+
Debug.logError("Path traversal attempt detected in create new image thumbnail, productId: " + productId, MODULE);
772+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
773+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale));
774+
}
732775
String imageType = ".jpg";
733776
int resizeWidth = Integer.parseInt(width);
734777
int resizeHeight = resizeWidth;
@@ -801,6 +844,14 @@ public static Map<String, Object> resizeImageOfProduct(DispatchContext dctx, Map
801844
String width = (String) context.get("resizeWidth");
802845
int resizeWidth = Integer.parseInt(width);
803846
int resizeHeight = resizeWidth;
847+
// Guard against path traversal via productId
848+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
849+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
850+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
851+
Debug.logError("Path traversal attempt detected in resize image, productId: " + productId, MODULE);
852+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
853+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale));
854+
}
804855

805856
try {
806857
BufferedImage bufImg = ImageIO.read(new File(imageServerPath + "/" + productId + "/" + dataResourceName));
@@ -831,6 +882,14 @@ public static Map<String, Object> renameImage(DispatchContext dctx, Map<String,
831882
String productId = (String) context.get("productId");
832883
String contentId = (String) context.get("contentId");
833884
String filenameToUse = (String) context.get("drDataResourceName");
885+
// Guard against path traversal via productId
886+
Path imageServerNormalizedPath = Paths.get(imageServerPath).normalize();
887+
Path resolvedProductDir = Paths.get(imageServerPath, productId).normalize();
888+
if (!resolvedProductDir.startsWith(imageServerNormalizedPath)) {
889+
Debug.logError("Path traversal attempt detected in rename image, productId: " + productId, MODULE);
890+
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ERROR,
891+
"ProductImageViewUnableWriteFile", UtilMisc.toMap("fileName", resolvedProductDir.toString()), locale));
892+
}
834893
String imageType = filenameToUse.substring(filenameToUse.lastIndexOf('.'));
835894
String imgExtension = filenameToUse.substring(filenameToUse.length() - 3, filenameToUse.length());
836895
String imageUrl = imageServerUrl + "/" + productId + "/" + filenameToUse;

0 commit comments

Comments
 (0)