CVE-2023-31544: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A stored cross-site scripting (XSS) vulnerability in alkacon-OpenCMS v11.0.0.0 allows attackers to execute arbitrary web scripts or HTML via a crafted payload injected into the Title field under the Upload Image module.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-05-16T21:15Z
Repository: https://github.com/alkacon/opencms-core
Language: Java
Files changed: src-gwt/org/opencms/ade/galleries/client/ui/CmsResultItemWidget.java
Commit date: 2019-08-27 12:56:35 +0200
Commit message: Fixed XSS issue in gallery result view (github issue #652).
Diff: diff --git a/src-gwt/org/opencms/ade/galleries/client/ui/CmsResultItemWidget.java b/src-gwt/org/opencms/ade/galleries/client/ui/CmsResultItemWidget.java
index 8ae83a6661..a41d72113a 100644
--- a/src-gwt/org/opencms/ade/galleries/client/ui/CmsResultItemWidget.java
+++ b/src-gwt/org/opencms/ade/galleries/client/ui/CmsResultItemWidget.java
@@ -37,10 +37,12 @@ import org.opencms.gwt.client.ui.input.CmsLabel.I_TitleGenerator;
 import org.opencms.gwt.client.util.CmsClientStringUtil;
 import org.opencms.gwt.client.util.CmsToolTipHandler;
 import org.opencms.gwt.shared.CmsAdditionalInfoBean;
 import org.opencms.gwt.shared.CmsListInfoBean;
 
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.ui.HTML;
 
 /**
  * The result list item widget.<p>
  *
@@ -153,10 +155,17 @@ public class CmsResultItemWidget extends CmsListItemWidget {
             m_contentPanel.insert(imageTile, 0);
         }
 
     }
 
+    private static Element appendDom(Element parent, String name) {
+
+        Element child = DOM.createElement(name);
+        parent.appendChild(child);
+        return child;
+    }
+
     /**
      * Gets the image tile.<p>
      *
      * @return the image tile
      */
@@ -211,20 +220,21 @@ public class CmsResultItemWidget extends CmsListItemWidget {
      *
      * @return the generated HTML
      */
     private String generateTooltipHtml(CmsListInfoBean infoBean) {
 
-        StringBuffer result = new StringBuffer();
-        result.append("<p><b>").append(CmsClientStringUtil.shortenString(infoBean.getTitle(), 70)).append("</b></p>");
+        Element root = DOM.createElement("div");
+        appendDom(appendDom(root, "p"), "b").setInnerText(CmsClientStringUtil.shortenString(infoBean.getTitle(), 70));
         if (infoBean.hasAdditionalInfo()) {
             for (CmsAdditionalInfoBean additionalInfo : infoBean.getAdditionalInfo()) {
-                result.append("<p>").append(additionalInfo.getName()).append(":&nbsp;");
-                // shorten the value to max 45 characters
-                result.append(CmsClientStringUtil.shortenString(additionalInfo.getValue(), 45)).append("</p>");
+                appendDom(root, "p").setInnerText(
+                    additionalInfo.getName()
+                        + ":\u00a0"
+                        + CmsClientStringUtil.shortenString(additionalInfo.getValue(), 45));
             }
         }
-        return result.toString();
+        return root.getInnerHTML();
     }
 
     /**
      * Returns the scale parameter for big thumbnail images.<p>
      *

Vulnerable code:         StringBuffer result = new StringBuffer();
        result.append("<p><b>").append(CmsClientStringUtil.shortenString(infoBean.getTitle(), 70)).append("</b></p>");
                result.append("<p>").append(additionalInfo.getName()).append(":&nbsp;");
                // shorten the value to max 45 characters
                result.append(CmsClientStringUtil.shortenString(additionalInfo.getValue(), 45)).append("</p>");
        return result.toString();
Fixed code: import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
    private static Element appendDom(Element parent, String name) {

        Element child = DOM.createElement(name);
        parent.appendChild(child);
        return child;
    }

        Element root = DOM.createElement("div");
        appendDom(appendDom(root, "p"), "b").setInnerText(CmsClientStringUtil.shortenString(infoBean.getTitle(), 70));
                appendDom(root, "p").setInnerText(
                    additionalInfo.getName()
                        + ":\u00a0"
                        + CmsClientStringUtil.shortenString(additionalInfo.getValue(), 45));
        return root.getInnerHTML();