CVE-2023-5556: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Reflected in GitHub repository structurizr/onpremises prior to 3194.
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: 6.1
Published: 2023-10-12T11:15Z
Repository: https://github.com/structurizr/onpremises
Language: Other
Files changed: structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-summary.jsp
Commit date: 2023-10-06 09:04:32 +0200
Commit message: XSS prevention.

Diff: diff --git a/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-summary.jsp b/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-summary.jsp
index eb4d0e9..206bb76 100644
--- a/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-summary.jsp
+++ b/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-summary.jsp
@@ -39,11 +39,11 @@
                 Workspace
             </div>
 
             <c:if test="${not empty param.version}">
             <div class="navigationItem">
-                <span class="label label-version"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-xs icon-white" /> ${workspace.internalVersion}</span>
+                <span class="label label-version"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-xs icon-white" /> <c:out value="${workspace.internalVersion}" /></span>
             </div>
             </c:if>
 
             <c:if test="${workspace.clientEncrypted}">
             <div class="navigationItem">
@@ -71,34 +71,34 @@
             <div class="navigationItemSeparator"></div>
             </c:if>
             
             <c:if test="${fn:startsWith(urlPrefix, '/workspace') && structurizrConfiguration.dslEditorEnabled}">
             <div class="navigationItem dslEditorNavigation">
-                <a href="${urlPrefix}/dsl${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-sm" /> DSL editor</a>
+                <a href="<c:out value="${urlPrefix}" />/dsl<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-sm" /> DSL editor</a>
             </div>
             </c:if>
 
             <div id="diagramsLink" class="navigationItem hidden">
-            <a href="${urlPrefix}/diagrams${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/bounding-box.svg" class="icon-sm" /> Diagrams</a>
+            <a href="<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/bounding-box.svg" class="icon-sm" /> Diagrams</a>
             </div>
 
             <div id="documentationLink"  class="navigationItem hidden">
-                <a href="${urlPrefix}/documentation${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/book.svg" class="icon-sm" /> Documentation</a>
+                <a href="<c:out value="${urlPrefix}" />/documentation<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/book.svg" class="icon-sm" /> Documentation</a>
             </div>
             <div id="decisionsLink" class="navigationItem hidden">
-                <a href="${urlPrefix}/decisions${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/journal-text.svg" class="icon-sm" /> Decisions</a>
+                <a href="<c:out value="${urlPrefix}" />/decisions<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/journal-text.svg" class="icon-sm" /> Decisions</a>
             </div>
             <div id="exploreLink" class="navigationItem hidden">
-                <a href="${urlPrefix}/explore${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/diagram-3.svg" class="icon-sm" /> Explore</a>
+                <a href="<c:out value="${urlPrefix}" />/explore<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/diagram-3.svg" class="icon-sm" /> Explore</a>
             </div>
 
             <div id="themeLink" class="navigationItem hidden">
-                <a href="${urlPrefix}/theme${urlSuffix}" target="_blank"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/palette.svg" class="icon-sm" /> Theme</a>
+                <a href="<c:out value="${urlPrefix}" />/theme<c:out value="${urlSuffix}" />" target="_blank"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/palette.svg" class="icon-sm" /> Theme</a>
             </div>
 
             <div id="imagesLink" class="navigationItem hidden">
-                <a href="${urlPrefix}/images"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/filetype-png.svg" class="icon-sm" /> Published images</a>
+                <a href="<c:out value="${urlPrefix}" />/images"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/filetype-png.svg" class="icon-sm" /> Published images</a>
             </div>
 
             <div id="exportJsonLinkNavItem" class="navigationItem">
                 <a id="exportJsonLink" href=""><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/filetype-json.svg" class="icon-sm" /> Export JSON</a>
             </div>
@@ -115,21 +115,21 @@
             </c:if>
 
             <div class="navigationItemSeparator"></div>
             <c:if test="${fn:startsWith(urlPrefix, '/workspace')}">
             <div class="navigationItem">
-                <a href="${urlPrefix}/settings"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-sm" /> Settings</a>
+                <a href="<c:out value="${urlPrefix}" />/settings"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-sm" /> Settings</a>
             </div>
 
             <c:if test="${workspace.editable eq true and workspace.locked eq true}">
             <div class="navigationItem">
                 <a id="unlockWorkspaceLink" href=""><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/unlock.svg" class="icon-sm" /> Unlock</a>
             </div>
             </c:if>
 
             <div class="navigationItem">
-                <a href="${urlPrefix}/users"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/people.svg" class="icon-sm" /> Users</a>
+                <a href="<c:out value="${urlPrefix}" />/users"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/people.svg" class="icon-sm" /> Users</a>
             </div>
             </c:if>
         </div>
     </div>
 
@@ -156,20 +156,20 @@
                     </c:if>
                 </p>
 
                 <c:if test="${fn:startsWith(urlPrefix, '/workspace') && not empty versions}">
                 <div class="centered" style="margin-top: 20px">
-                    <form class="form-inline" style="display: inline-block" method="get" action="${urlPrefix}">
+                    <form class="form-inline" style="display: inline-block" method="get" action="<c:out value="${urlPrefix}" />">
                         <select id="workspaceVersion" name="version" class="form-control">
                             <c:forEach var="version" items="${versions}">
                                 <option value="${version.versionId}"><fmt:formatDate value="${version.lastModifiedDate}" pattern="EEE dd MMM yyyy HH:mm:ss z" timeZone="${user.timeZone}" /></option>
                             </c:forEach>
                         </select>
                         <input type="submit" class="btn btn-default" value="Load version" style="height: 35.5px"/>
                     </form>
                     <c:if test="${not empty param.version && workspace.editable && not workspace.locked}">
-                        <button id="revertButton" class="btn btn-default small"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-btn" /> Revert to ${workspace.internalVersion}</button>
+                        <button id="revertButton" class="btn btn-default small"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-btn" /> Revert to <c:out value="${workspace.internalVersion}" /></button>
                     </c:if>
                 </div>
                 </c:if>
 
                 <div id="gettingStarted" class="centered hidden">
@@ -184,11 +184,11 @@
 
                     <div class="row" style="margin-top: 20px; margin-bottom: 20px">
                         <div class="col-sm-4 centered">
                             <div style="padding: 10px; margin: 10px">
                                 <div style="margin-top: 5px; font-size: 20px">
-                                    <a href="${urlPrefix}/dsl"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-lg"/> DSL editor</a>
+                                    <a href="<c:out value="${urlPrefix}" />/dsl"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-lg"/> DSL editor</a>
                                 </div>
                                 <div class="small">
                                     Open the online DSL editor, and create your workspace using the Structurizr DSL.
                                 </div>
                             </div>
@@ -204,11 +204,11 @@
                             </div>
                         </div>
                         <div class="col-sm-4 centered">
                             <div style="padding: 10px; margin: 10px">
                                 <div style="margin-top: 5px; font-size: 20px">
-                                    <a href="${urlPrefix}/settings#api"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-lg" /> Settings</a>
+                                    <a href="<c:out value="${urlPrefix}" />/settings#api"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-lg" /> Settings</a>
                                 </div>
                                 <div class="small">
                                     Use the workspace settings page to find your API key/secret, for uploading your workspace with the Structurizr CLI or code-based libraries.
                                 </div>
                             </div>
@@ -234,11 +234,11 @@
     progressMessage.show('<p>Loading workspace...</p>');
 
     $('#unlockWorkspaceLink').click(function(event) { unlockWorkspace(event); });
 
     <c:if test="${not empty param.version}">
-    $('#workspaceVersion').val("${workspace.internalVersion}");
+    $('#workspaceVersion').val("<c:out value="${workspace.internalVersion}" />");
     </c:if>
 
     function addOnClickHandler(domId, handler) {
         var element = document.getElementById(domId);
         if (element) {
@@ -298,11 +298,11 @@
             var views = structurizr.workspace.getViews();
             const thumbnailSize = 200;
             var html = '';
 
             views.forEach(function(view) {
-                var url = '${urlPrefix}/diagrams${urlSuffix}#' + structurizr.util.escapeHtml(view.key);
+                var url = '<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />#' + structurizr.util.escapeHtml(view.key);
                 var title = structurizr.util.escapeHtml(structurizr.ui.getTitleForView(view));
 
                 if (count <= maxNumberOfViews) {
                     html += '<div class="centered" style="display: inline-block; margin: 10px 10px 40px 10px; width: ' + thumbnailSize + 'px;">';
 
@@ -327,11 +327,11 @@
                 }
                 count++;
             });
 
             if (views.length > maxNumberOfViews) {
-                html += '<div class="small"><a href="${urlPrefix}/diagrams${urlSuffix}">More diagrams...</a></div>';
+                html += '<div class="small"><a href="<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />">More diagrams...</a></div>';
             }
 
             diagramsDiv.addClass('centered');
             diagramsDiv.append(html);
 

Vulnerable code:                 <span class="label label-version"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-xs icon-white" /> ${workspace.internalVersion}</span>
                <a href="${urlPrefix}/dsl${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-sm" /> DSL editor</a>
            <a href="${urlPrefix}/diagrams${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/bounding-box.svg" class="icon-sm" /> Diagrams</a>
                <a href="${urlPrefix}/documentation${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/book.svg" class="icon-sm" /> Documentation</a>
                <a href="${urlPrefix}/decisions${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/journal-text.svg" class="icon-sm" /> Decisions</a>
                <a href="${urlPrefix}/explore${urlSuffix}"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/diagram-3.svg" class="icon-sm" /> Explore</a>
                <a href="${urlPrefix}/theme${urlSuffix}" target="_blank"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/palette.svg" class="icon-sm" /> Theme</a>
                <a href="${urlPrefix}/images"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/filetype-png.svg" class="icon-sm" /> Published images</a>
                <a href="${urlPrefix}/settings"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-sm" /> Settings</a>
                <a href="${urlPrefix}/users"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/people.svg" class="icon-sm" /> Users</a>
                    <form class="form-inline" style="display: inline-block" method="get" action="${urlPrefix}">
                        <button id="revertButton" class="btn btn-default small"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-btn" /> Revert to ${workspace.internalVersion}</button>
                                    <a href="${urlPrefix}/dsl"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-lg"/> DSL editor</a>
                                    <a href="${urlPrefix}/settings#api"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-lg" /> Settings</a>
    $('#workspaceVersion').val("${workspace.internalVersion}");
                var url = '${urlPrefix}/diagrams${urlSuffix}#' + structurizr.util.escapeHtml(view.key);
                html += '<div class="small"><a href="${urlPrefix}/diagrams${urlSuffix}">More diagrams...</a></div>';
Fixed code:                 <span class="label label-version"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-xs icon-white" /> <c:out value="${workspace.internalVersion}" /></span>
                <a href="<c:out value="${urlPrefix}" />/dsl<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-sm" /> DSL editor</a>
            <a href="<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/bounding-box.svg" class="icon-sm" /> Diagrams</a>
                <a href="<c:out value="${urlPrefix}" />/documentation<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/book.svg" class="icon-sm" /> Documentation</a>
                <a href="<c:out value="${urlPrefix}" />/decisions<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/journal-text.svg" class="icon-sm" /> Decisions</a>
                <a href="<c:out value="${urlPrefix}" />/explore<c:out value="${urlSuffix}" />"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/diagram-3.svg" class="icon-sm" /> Explore</a>
                <a href="<c:out value="${urlPrefix}" />/theme<c:out value="${urlSuffix}" />" target="_blank"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/palette.svg" class="icon-sm" /> Theme</a>
                <a href="<c:out value="${urlPrefix}" />/images"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/filetype-png.svg" class="icon-sm" /> Published images</a>
                <a href="<c:out value="${urlPrefix}" />/settings"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-sm" /> Settings</a>
                <a href="<c:out value="${urlPrefix}" />/users"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/people.svg" class="icon-sm" /> Users</a>
                    <form class="form-inline" style="display: inline-block" method="get" action="<c:out value="${urlPrefix}" />">
                        <button id="revertButton" class="btn btn-default small"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/clock-history.svg" class="icon-btn" /> Revert to <c:out value="${workspace.internalVersion}" /></button>
                                    <a href="<c:out value="${urlPrefix}" />/dsl"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/code-slash.svg" class="icon-lg"/> DSL editor</a>
                                    <a href="<c:out value="${urlPrefix}" />/settings#api"><img src="${structurizrConfiguration.cdnUrl}/bootstrap-icons/gear.svg" class="icon-lg" /> Settings</a>
    $('#workspaceVersion').val("<c:out value="${workspace.internalVersion}" />");
                var url = '<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />#' + structurizr.util.escapeHtml(view.key);
                html += '<div class="small"><a href="<c:out value="${urlPrefix}" />/diagrams<c:out value="${urlSuffix}" />">More diagrams...</a></div>';