CVE-2026-53698
Description
Silverpeas through 6.4.6 mishandles the "Personal space" feature that is selected when no componentId is set.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <=6.4.6
Patches
1caa6e6d1ac96Fix bug #15229
2 files changed · +63 −111
core-library/src/main/java/org/silverpeas/core/util/file/FileServerUtils.java+12 −70 modified@@ -32,12 +32,9 @@ import java.net.URI; import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; /** * @author NEY - * @version */ public class FileServerUtils { @@ -48,29 +45,13 @@ public class FileServerUtils { public static final String DIR_TYPE_PARAMETER = "DirType"; public static final String USER_ID_PARAMETER = "UserId"; public static final String MIME_TYPE_PARAMETER = "MimeType"; - public static final String TYPE_UPLOAD_PARAMETER = "TypeUpload"; public static final String NODE_ID_PARAMETER = "NodeId"; public static final String PUBLICATION_ID_PARAMETER = "PubId"; public static final String SIZE_PARAMETER = "Size"; private static final SettingBundle lookSettings = ResourceLocator.getSettingBundle("org.silverpeas.lookAndFeel.generalLook"); - /** - * Replace chars that have special meanings in url by their http substitute. - * - * @param toParse the string which chars that have special meanings in url by their http - * substitute. - * @return a string without url meaning chars. - */ - public static String replaceSpecialChars(String toParse) { - String newLogicalName = toParse.replace("#", "%23"); - newLogicalName = newLogicalName.replace("%", "%25"); - newLogicalName = newLogicalName.replace("&", "%26"); - newLogicalName = newLogicalName.replace(";", "%3B"); - return newLogicalName; - } - /** * Replace accented chars from a string. * @@ -103,27 +84,6 @@ public static String replaceAccentChars(String toParse) { return newLogicalName; } - /** - * Return the full url to access an attachment from web site - * - * - * @param componentId - * @param logicalName - * @param physicalName - * @param mimeType - * @param subDirectory - * @return - */ - public static String getWebUrl(String componentId, String logicalName, String physicalName, - String mimeType, String subDirectory) { - StringBuilder url = new StringBuilder(); - String newLogicalName = URLEncoder.encodePathParamValue(logicalName); - url.append(newLogicalName).append("?ComponentId=").append(componentId). - append("&SourceFile=").append(physicalName).append("&MimeType=").append( - mimeType).append("&Directory=").append(subDirectory); - return url.toString(); - } - public static String getUrl(String componentId, String logicalName) { StringBuilder url = new StringBuilder(); String newLogicalName = URLEncoder.encodePathSegment(logicalName); @@ -161,7 +121,7 @@ public static String getAttachmentURL(String componentId, String logicalName, St language = I18NHelper.DEFAULT_LANGUAGE; } url.append("/attached_file/").append("componentId/").append(URLEncoder.encodePathSegment( - componentId)).append("/attachmentId/").append(URLEncoder.encodePathSegment(attachmentId)). + componentId)).append("/attachmentId/").append(URLEncoder.encodePathSegment(attachmentId)). append("/lang/").append(URLEncoder.encodePathSegment(language)).append("/name/"). append(newLogicalName); return url.toString(); @@ -173,14 +133,15 @@ public static String getAttachmentURL(String componentId, String logicalName, St * Each image uploaded in Silverpeas are kept with their original size. From them, a set of * resized images are computed. This method is to get the URL of the resized version of an * uploaded image. + * * @param originalImageURL the URL of the original, non-resized, image. * @param sizeParams the size of the image to get. The size can be specified either a key in the * {@code org.silverpeas.lookAndFeel.generalLook} bundle or as a dimension. The keys of the - * properties indicating an image size are always prefixed by the 'image.size' term. The - * dimension of an image must be in the form of WIDTHxHEIGHT with WIDTH the width in pixels of + * properties indicating an image size are always prefixed by the 'image.size' term. The dimension + * of an image must be in the form of <code>WIDTHxHEIGHT</code> with WIDTH the width in pixels of * the image and HEIGHT the height in pixels of the image. WIDTH or HEIGHT can be omitted but the - * 'x' character is required. If null, empty or or not well formed, the original image URL is - * then returned. + * 'x' character is required. If null, empty or not well-formed, the original image URL is then + * returned. * @return the URL of the image with the specified size. */ public static String getImageURL(String originalImageURL, String sizeParams) { @@ -212,29 +173,9 @@ public static String getImageURL(String originalImageURL, String sizeParams) { return resizedImagePath; } - public static String getAliasURL(String componentId, String logicalName, String attachmentId) { - StringBuilder url = new StringBuilder(); - String newLogicalName = URLEncoder.encodePathSegment(logicalName); - url.append(getApplicationContext()).append("/AliasFileServer/").append(newLogicalName). - append("?ComponentId=").append(componentId).append("&AttachmentId="). - append(attachmentId); - return url.toString(); - } - - public static Map<String, String> getMappedUrl(String spaceId, String componentId, - String logicalName, String physicalName, String mimeType, String subDirectory) { - Map<String, String> parameters = new HashMap<String, String>(); - parameters.put("SpaceId", spaceId); - parameters.put("ComponentId", componentId); - parameters.put("SourceFile", physicalName); - parameters.put("MimeType", mimeType); - parameters.put("Directory", subDirectory); - return parameters; - } - - public static String getUrl(String componentId, String name, String mimeType, String subDirectory) { - String url = getUrl(componentId, name, name, mimeType, subDirectory); - return url; + public static String getUrl(String componentId, String name, String mimeType, + String subDirectory) { + return getUrl(componentId, name, name, mimeType, subDirectory); } public static String getUrl(String logicalName, String physicalName, String componentId) { @@ -255,9 +196,10 @@ public static String getUrl(String componentId, String userId, String logicalNam } String newLogicalName = URLEncoder.encodePathSegment(logicalName); url.append(getApplicationContext()).append("/FileServer/").append(newLogicalName).append( - "?ComponentId=").append(componentId).append("&UserId=").append(userId). + "?ComponentId=").append(componentId).append("&UserId=").append(userId). append("&SourceFile=").append(URLEncoder.encodePathParamValue(physicalName)).append( - "&MimeType=").append(mimeType).append("&ArchiveIt=").append(archiveItStr).append("&PubId="). + "&MimeType=").append(mimeType).append("&ArchiveIt=").append(archiveItStr).append( + "&PubId="). append(pubId).append("&NodeId=").append(nodeId).append("&Directory=").append(subDirectory); return url.toString(); }
core-war/src/main/java/org/silverpeas/web/servlets/FileServer.java+51 −41 modified@@ -25,21 +25,22 @@ import org.silverpeas.core.ResourceReference; import org.silverpeas.core.admin.service.OrganizationController; +import org.silverpeas.core.annotation.Bean; import org.silverpeas.core.contribution.content.LinkUrlDataSource; import org.silverpeas.core.contribution.content.LinkUrlDataSourceScanner; import org.silverpeas.core.contribution.content.wysiwyg.service.directive.ImageUrlAccordingToHtmlSizeDirective; import org.silverpeas.core.io.file.SilverpeasFile; import org.silverpeas.core.io.file.SilverpeasFileDescriptor; import org.silverpeas.core.io.file.SilverpeasFileProvider; import org.silverpeas.core.silverstatistics.access.service.StatisticService; -import org.silverpeas.kernel.bundle.ResourceLocator; -import org.silverpeas.kernel.bundle.SettingBundle; -import org.silverpeas.kernel.util.StringUtil; import org.silverpeas.core.util.URLUtil; -import org.silverpeas.kernel.logging.SilverLogger; import org.silverpeas.core.web.http.HttpRequest; import org.silverpeas.core.web.mvc.AbstractFileSender; import org.silverpeas.core.web.mvc.controller.MainSessionController; +import org.silverpeas.kernel.bundle.ResourceLocator; +import org.silverpeas.kernel.bundle.SettingBundle; +import org.silverpeas.kernel.logging.SilverLogger; +import org.silverpeas.kernel.util.StringUtil; import javax.activation.FileDataSource; import javax.inject.Inject; @@ -58,8 +59,8 @@ import static org.silverpeas.core.contribution.content.LinkUrlDataSourceScanner.extractUrlParameters; import static org.silverpeas.core.util.StringDataExtractor.RegexpPatternDirective.regexp; import static org.silverpeas.core.util.StringDataExtractor.from; -import static org.silverpeas.kernel.util.StringUtil.defaultStringIfNotDefined; import static org.silverpeas.core.util.file.FileServerUtils.*; +import static org.silverpeas.kernel.util.StringUtil.defaultStringIfNotDefined; public class FileServer extends AbstractFileSender { @@ -68,27 +69,42 @@ public class FileServer extends AbstractFileSender { @Inject private OrganizationController organizationController; + @Inject + private StatisticService statistics; + @Override - public void doGet(HttpServletRequest req, HttpServletResponse res) { - doPost(req, res); + public void doPost(HttpServletRequest req, HttpServletResponse res) { + res.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } @Override - public void doPost(HttpServletRequest req, HttpServletResponse res) { + public void doGet(HttpServletRequest req, HttpServletResponse res) { try { final Map<String, String> params = HttpRequest.decorate(req).getParameterSimpleMap(); final String componentId = params.get(COMPONENT_ID_PARAMETER); + if (StringUtil.isNotDefined(componentId)) { + res.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + final HttpSession session = req.getSession(true); final MainSessionController mainSessionCtrl = (MainSessionController) session.getAttribute( MainSessionController.MAIN_SESSION_CONTROLLER_ATT); - if ((mainSessionCtrl == null) || (!isUserAllowed(mainSessionCtrl, componentId))) { + if (mainSessionCtrl == null) { + var generalSettings = ResourceLocator.getGeneralSettingBundle(); SilverLogger.getLogger(this) .warn("Session timeout after {1}. New session id: {0}", session.getId(), - ResourceLocator.getGeneralSettingBundle().getString("sessionTimeout")); - res.sendRedirect(URLUtil.getApplicationURL() + - ResourceLocator.getGeneralSettingBundle().getString("sessionTimeout")); + generalSettings.getString("sessionTimeout")); + res.sendRedirect(URLUtil.getApplicationURL() + generalSettings.getString("sessionTimeout")); + return; + } + if (!isUserAllowed(mainSessionCtrl, componentId)) { + SilverLogger.getLogger(this).warn("Component instance {0}: access forbidden for user {1}", + componentId, mainSessionCtrl.getUserId()); + res.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } + final SilverpeasFile file = getSilverpeasFile(params); sendFile(req, res, file); final String archiveIt = params.get(ARCHIVE_IT_PARAMETER); @@ -109,35 +125,32 @@ private static SilverpeasFile getSilverpeasFile(final Map<String, String> params final String componentId = params.get(COMPONENT_ID_PARAMETER); final String mimeType = params.get(MIME_TYPE_PARAMETER); final String dirType = params.get(DIR_TYPE_PARAMETER); - final String typeUpload = params.get(TYPE_UPLOAD_PARAMETER); final String size = params.get(SIZE_PARAMETER); - String sourceFile = params.get(SOURCE_FILE_PARAMETER); - if (StringUtil.isDefined(size)) { - sourceFile = size + File.separatorChar + sourceFile; - } + String sourceFile = StringUtil.isDefined(size) ? + size + File.separatorChar + params.get(SOURCE_FILE_PARAMETER) : + params.get(SOURCE_FILE_PARAMETER); + SilverpeasFileDescriptor descriptor = - new SilverpeasFileDescriptor(componentId).fileName(sourceFile).mimeType(mimeType); - if (typeUpload != null) { - descriptor.absolutePath(); - } else { - if (dirType != null) { - if (dirType.equals( - ResourceLocator.getGeneralSettingBundle().getString("RepositoryTypeTemp"))) { - descriptor = descriptor.temporaryFile(); - } - } else { - String directory = params.get(DIRECTORY_PARAMETER); - descriptor = descriptor.parentDirectory(directory); + new SilverpeasFileDescriptor(componentId) + .fileName(sourceFile) + .mimeType(mimeType); + if (dirType != null) { + if (dirType.equals( + ResourceLocator.getGeneralSettingBundle().getString("RepositoryTypeTemp"))) { + descriptor = descriptor.temporaryFile(); } + } else { + String directory = params.get(DIRECTORY_PARAMETER); + descriptor = descriptor.parentDirectory(directory); } + return SilverpeasFileProvider.getFile(descriptor); } private void addStatistic(final String userId, final String nodeId, final ResourceReference pubPK) { try { - StatisticService statisticService = StatisticService.get(); - statisticService.addStat(userId, pubPK, 1, "Publication"); + statistics.addStat(userId, pubPK, 1, "Publication"); } catch (Exception ex) { SilverLogger.getLogger(this) .error("Cannot write statistics about publication " + pubPK + " in node " + nodeId, ex); @@ -147,18 +160,13 @@ private void addStatistic(final String userId, final String nodeId, // check if the user is allowed to access the required component private boolean isUserAllowed(MainSessionController controller, String componentId) { boolean isAllowed; - if (componentId == null) { - // Personal space + if ("yes".equalsIgnoreCase( + controller.getComponentParameterValue(componentId, "publicFiles"))) { + // Case of file contained in a component used as a file storage isAllowed = true; } else { - if ("yes".equalsIgnoreCase( - controller.getComponentParameterValue(componentId, "publicFiles"))) { - // Case of file contained in a component used as a file storage - isAllowed = true; - } else { - isAllowed = - organizationController.isComponentAvailableToUser(componentId, controller.getUserId()); - } + isAllowed = + organizationController.isComponentAvailableToUser(componentId, controller.getUserId()); } return isAllowed; } @@ -169,6 +177,7 @@ protected SettingBundle getSettingBunde() { "org.silverpeas.util.peasUtil.multiLang.fileServerBundle"); } + @Bean @Singleton public static class ImageUrlToDataSourceScanner implements LinkUrlDataSourceScanner { @@ -191,6 +200,7 @@ public List<LinkUrlDataSource> scanHtml(final String htmlContent) { } } + @Bean @Singleton public static class ImageUrlAccordingToHtmlSizeDirectiveTranslator extends ImageUrlAccordingToHtmlSizeDirective.SrcWithSizeParametersTranslator {
Vulnerability mechanics
Root cause
"The application does not properly validate the componentId when accessing the "Personal space" feature."
Attack vector
An attacker with low privileges can exploit this vulnerability by crafting a request to the FileServer servlet. If the `componentId` parameter is not set, the application defaults to accessing the user's personal space. This allows unauthorized access to files that should be protected. The vulnerability is triggered when no `componentId` is provided in the request [ref_id=1].
Affected code
The vulnerability resides in the `FileServer.java` class, specifically within the `doGet` method. The `getSilverpeasFile` method is also involved in processing file requests. The patch modifies the `FileServer.java` file to add input validation for the `componentId` parameter.
What the fix does
The patch modifies the `doGet` method in `FileServer.java` to add a check for a missing `componentId` parameter. If `componentId` is not defined, the servlet now returns a bad request status instead of proceeding to access the personal space. This prevents unauthorized access by ensuring that a valid component is always specified for file access [patch_id=5502023].
Preconditions
- authThe attacker must have low privileges.
Generated on Jun 10, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/Silverpeas/Silverpeas-Core/blob/983c5d07928b8a5ddcb39cc17d7fb9a0d87019b9/core-war/src/main/java/org/silverpeas/web/servlets/FileServer.javanvd
- github.com/Silverpeas/Silverpeas-Core/blob/983c5d07928b8a5ddcb39cc17d7fb9a0d87019b9/core-war/src/main/java/org/silverpeas/web/servlets/FileServer.javanvd
- github.com/Silverpeas/Silverpeas-Core/commit/caa6e6d1ac967ebd29b39e11c2ef5e7fd0047eecnvd
- tracker.silverpeas.org/issues/15229nvd
News mentions
0No linked articles in our index yet.