VYPR
Moderate severityNVD Advisory· Published Mar 20, 2024· Updated Aug 1, 2024

GeoServer Stored Cross-Site Scripting (XSS) vulnerability in WMS OpenLayers Format

CVE-2024-23818

Description

GeoServer is an open source software server written in Java that allows users to share and edit geospatial data. A stored cross-site scripting (XSS) vulnerability exists in versions prior to 2.23.3 and 2.24.1 that enables an authenticated administrator with workspace-level privileges to store a JavaScript payload in the GeoServer catalog that will execute in the context of another user's browser when viewed in the WMS GetMap OpenLayers Output Format. Access to the WMS OpenLayers Format is available to all users by default although data and service security may limit users' ability to trigger the XSS. Versions 2.23.3 and 2.24.1 contain a patch for this issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.geoserver:gs-wmsMaven
< 2.23.32.23.3
org.geoserver:gs-wmsMaven
>= 2.24.0, < 2.24.12.24.1

Affected products

1

Patches

2
a26c32a469ee

[GEOS-11153] Improve handling special characters in the WMS OpenLayers Format (#7193)

https://github.com/geoserver/geoserverAndrea AimeOct 16, 2023via ghsa
4 files changed · +48 33
  • src/wms/src/main/java/org/geoserver/wms/map/AbstractOpenLayersMapOutputFormat.java+2 0 modified
    @@ -7,6 +7,7 @@
     
     import static org.geoserver.template.TemplateUtils.FM_VERSION;
     
    +import freemarker.core.HTMLOutputFormat;
     import freemarker.ext.beans.BeansWrapper;
     import freemarker.template.Configuration;
     import freemarker.template.Template;
    @@ -107,6 +108,7 @@ public abstract class AbstractOpenLayersMapOutputFormat implements GetMapOutputF
             BeansWrapper bw = new BeansWrapper(FM_VERSION);
             bw.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
             cfg.setObjectWrapper(bw);
    +        cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
         }
     
         /** wms configuration */
    
  • src/wms/src/main/resources/org/geoserver/wms/map/OpenLayers2MapTemplate.ftl+5 3 modified
    @@ -106,6 +106,7 @@
             <script src="${relBaseUrl}/openlayers/OpenLayers.js" type="text/javascript">
             </script>
             <script defer="defer" type="text/javascript">
    +        <#outputformat "JavaScript">
                 var map;
                 var untiled;
                 var tiled;
    @@ -149,7 +150,7 @@
                 
                     // setup tiled layer
                     tiled = new OpenLayers.Layer.WMS(
    -                    "${layerName} - Tiled", "${baseUrl}/${servicePath}",
    +                    "${layerName?js_string} - Tiled", "${baseUrl}/${servicePath?js_string}",
                         {
                             <#list parameters as param>
                             "${param.name?js_string}": '${param.value?js_string}',
    @@ -168,7 +169,7 @@
                       
                     // setup single tiled layer
                     untiled = new OpenLayers.Layer.WMS(
    -                    "${layerName} - Untiled", "${baseUrl}/${servicePath}",
    +                    "${layerName?js_string} - Untiled", "${baseUrl}/${servicePath?js_string}",
                         {
                             <#list parameters as param>
                             "${param.name?js_string}": '${param.value?js_string}',
    @@ -242,7 +243,7 @@
                         if(map.layers[0].params.FEATUREID) {
                             params.featureid = map.layers[0].params.FEATUREID;
                         }
    -                    OpenLayers.loadURL("${baseUrl}/${servicePath}", params, this, setHTML, setHTML);
    +                    OpenLayers.loadURL("${baseUrl}/${servicePath?js_string}", params, this, setHTML, setHTML);
                         OpenLayers.Event.stop(e);
                     });
                 }
    @@ -447,6 +448,7 @@
                     tiled.mergeNewParams(params);
                     untiled.mergeNewParams(params);
                 }
    +        </#outputformat>
             </script>
         </head>
         <body onload="init()">
    
  • src/wms/src/main/resources/org/geoserver/wms/map/OpenLayers3MapTemplate.ftl+4 3 modified
    @@ -228,6 +228,7 @@
             <em>Click on the map to get feature info</em>
         </div>
         <script type="text/javascript">
    +    <#outputformat "JavaScript">
           var pureCoverage = ${pureCoverage?string};
           // if this is just a coverage or a group of them, disable a few items,
           // and default to jpeg format
    @@ -257,7 +258,7 @@
           var untiled = new ol.layer.Image({
             source: new ol.source.ImageWMS({
               ratio: 1,
    -          url: '${baseUrl}/${servicePath}',
    +          url: '${baseUrl}/${servicePath?js_string}',
               params: {'FORMAT': format,
                        'VERSION': '1.1.1',  
                  <#list parameters as param>
    @@ -269,7 +270,7 @@
           var tiled = new ol.layer.Tile({
             visible: false,
             source: new ol.source.TileWMS({
    -          url: '${baseUrl}/${servicePath}',
    +          url: '${baseUrl}/${servicePath?js_string}',
               params: {'FORMAT': format, 
                        'VERSION': '1.1.1',
                        tiled: true,
    @@ -450,7 +451,7 @@
               }
               map.updateSize()
             }
    -
    +    </#outputformat>
         </script>
       </body>
     </html>
    
  • src/wms/src/test/java/org/geoserver/wms/map/OpenLayersMapOutputFormatTest.java+37 27 modified
    @@ -130,22 +130,28 @@ public void testXssFix() throws Exception {
             StyleInfo styleByName = catalog.getStyleByName("Default");
             Style basicStyle = styleByName.getStyle();
             FeatureLayer layer = new FeatureLayer(fs, basicStyle);
    -        layer.setTitle("Title");
    +        layer.setTitle("Title</foo");
             map.addLayer(layer);
             request.setFormat("application/openlayers");
    -        String htmlDoc = getAsHTML(map);
    -        // check that weird param is correctly encoded to avoid js code execution
    -        int index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf(
    -                                "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'");
    -        assertTrue(index > -1);
    -        index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf("\"25064;ALERT(1)//419\": '1'");
    -        assertTrue(index > -1);
    +
    +        StyleInfo otherStyle = new StyleInfoImpl(null);
    +        otherStyle.setName("style<>");
    +        try {
    +            request.getLayers().get(0).getLayerInfo().getStyles().add(otherStyle);
    +            String htmlDoc = getAsHTML(map);
    +            // check that weird param is correctly encoded to avoid js code execution
    +            assertThat(
    +                    htmlDoc,
    +                    containsString(
    +                            "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'"));
    +            assertThat(htmlDoc, containsString("\"25064;ALERT(1)//419\": '1'"));
    +            assertThat(htmlDoc, not(containsString(layer.getTitle())));
    +            assertThat(htmlDoc, containsString("Title<\\/foo"));
    +            assertThat(htmlDoc, not(containsString(otherStyle.getName())));
    +            assertThat(htmlDoc, containsString("style&lt;&gt;"));
    +        } finally {
    +            request.getLayers().get(0).getLayerInfo().getStyles().remove(otherStyle);
    +        }
         }
     
         @Test
    @@ -472,19 +478,23 @@ public void testXssOL3() throws Exception {
             layer.setTitle("Title");
             map.addLayer(layer);
             request.setFormat("application/openlayers3");
    -        String htmlDoc = getAsHTMLOL3(map);
    -        // check that weird param is correctly encoded to avoid js code execution
    -        int index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf(
    -                                "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'");
    -        assertTrue(index > -1);
    -        index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf("\"25064;ALERT(1)//419\": '1'");
    -        assertTrue(index > -1);
    +
    +        StyleInfo otherStyle = new StyleInfoImpl(null);
    +        otherStyle.setName("style<>");
    +        try {
    +            request.getLayers().get(0).getLayerInfo().getStyles().add(otherStyle);
    +            String htmlDoc = getAsHTMLOL3(map);
    +            // check that weird param is correctly encoded to avoid js code execution
    +            assertThat(
    +                    htmlDoc,
    +                    containsString(
    +                            "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'"));
    +            assertThat(htmlDoc, containsString("\"25064;ALERT(1)//419\": '1'"));
    +            assertThat(htmlDoc, not(containsString(otherStyle.getName())));
    +            assertThat(htmlDoc, containsString("style&lt;&gt;"));
    +        } finally {
    +            request.getLayers().get(0).getLayerInfo().getStyles().remove(otherStyle);
    +        }
         }
     
         @Test
    
4557a832eed1

[GEOS-11153] Improve handling special characters in the WMS OpenLayers Format (#7194)

https://github.com/geoserver/geoserverAndrea AimeOct 16, 2023via ghsa
4 files changed · +48 33
  • src/wms/src/main/java/org/geoserver/wms/map/AbstractOpenLayersMapOutputFormat.java+2 0 modified
    @@ -7,6 +7,7 @@
     
     import static org.geoserver.template.TemplateUtils.FM_VERSION;
     
    +import freemarker.core.HTMLOutputFormat;
     import freemarker.ext.beans.BeansWrapper;
     import freemarker.template.Configuration;
     import freemarker.template.Template;
    @@ -107,6 +108,7 @@ public abstract class AbstractOpenLayersMapOutputFormat implements GetMapOutputF
             BeansWrapper bw = new BeansWrapper(FM_VERSION);
             bw.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
             cfg.setObjectWrapper(bw);
    +        cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
         }
     
         /** wms configuration */
    
  • src/wms/src/main/resources/org/geoserver/wms/map/OpenLayers2MapTemplate.ftl+5 3 modified
    @@ -106,6 +106,7 @@
             <script src="${relBaseUrl}/openlayers/OpenLayers.js" type="text/javascript">
             </script>
             <script defer="defer" type="text/javascript">
    +        <#outputformat "JavaScript">
                 var map;
                 var untiled;
                 var tiled;
    @@ -149,7 +150,7 @@
                 
                     // setup tiled layer
                     tiled = new OpenLayers.Layer.WMS(
    -                    "${layerName} - Tiled", "${baseUrl}/${servicePath}",
    +                    "${layerName?js_string} - Tiled", "${baseUrl}/${servicePath?js_string}",
                         {
                             <#list parameters as param>
                             "${param.name?js_string}": '${param.value?js_string}',
    @@ -168,7 +169,7 @@
                       
                     // setup single tiled layer
                     untiled = new OpenLayers.Layer.WMS(
    -                    "${layerName} - Untiled", "${baseUrl}/${servicePath}",
    +                    "${layerName?js_string} - Untiled", "${baseUrl}/${servicePath?js_string}",
                         {
                             <#list parameters as param>
                             "${param.name?js_string}": '${param.value?js_string}',
    @@ -242,7 +243,7 @@
                         if(map.layers[0].params.FEATUREID) {
                             params.featureid = map.layers[0].params.FEATUREID;
                         }
    -                    OpenLayers.loadURL("${baseUrl}/${servicePath}", params, this, setHTML, setHTML);
    +                    OpenLayers.loadURL("${baseUrl}/${servicePath?js_string}", params, this, setHTML, setHTML);
                         OpenLayers.Event.stop(e);
                     });
                 }
    @@ -447,6 +448,7 @@
                     tiled.mergeNewParams(params);
                     untiled.mergeNewParams(params);
                 }
    +        </#outputformat>
             </script>
         </head>
         <body onload="init()">
    
  • src/wms/src/main/resources/org/geoserver/wms/map/OpenLayers3MapTemplate.ftl+4 3 modified
    @@ -228,6 +228,7 @@
             <em>Click on the map to get feature info</em>
         </div>
         <script type="text/javascript">
    +    <#outputformat "JavaScript">
           var pureCoverage = ${pureCoverage?string};
           // if this is just a coverage or a group of them, disable a few items,
           // and default to jpeg format
    @@ -257,7 +258,7 @@
           var untiled = new ol.layer.Image({
             source: new ol.source.ImageWMS({
               ratio: 1,
    -          url: '${baseUrl}/${servicePath}',
    +          url: '${baseUrl}/${servicePath?js_string}',
               params: {'FORMAT': format,
                        'VERSION': '1.1.1',  
                  <#list parameters as param>
    @@ -269,7 +270,7 @@
           var tiled = new ol.layer.Tile({
             visible: false,
             source: new ol.source.TileWMS({
    -          url: '${baseUrl}/${servicePath}',
    +          url: '${baseUrl}/${servicePath?js_string}',
               params: {'FORMAT': format, 
                        'VERSION': '1.1.1',
                        tiled: true,
    @@ -450,7 +451,7 @@
               }
               map.updateSize()
             }
    -
    +    </#outputformat>
         </script>
       </body>
     </html>
    
  • src/wms/src/test/java/org/geoserver/wms/map/OpenLayersMapOutputFormatTest.java+37 27 modified
    @@ -130,22 +130,28 @@ public void testXssFix() throws Exception {
             StyleInfo styleByName = catalog.getStyleByName("Default");
             Style basicStyle = styleByName.getStyle();
             FeatureLayer layer = new FeatureLayer(fs, basicStyle);
    -        layer.setTitle("Title");
    +        layer.setTitle("Title</foo");
             map.addLayer(layer);
             request.setFormat("application/openlayers");
    -        String htmlDoc = getAsHTML(map);
    -        // check that weird param is correctly encoded to avoid js code execution
    -        int index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf(
    -                                "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'");
    -        assertTrue(index > -1);
    -        index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf("\"25064;ALERT(1)//419\": '1'");
    -        assertTrue(index > -1);
    +
    +        StyleInfo otherStyle = new StyleInfoImpl(null);
    +        otherStyle.setName("style<>");
    +        try {
    +            request.getLayers().get(0).getLayerInfo().getStyles().add(otherStyle);
    +            String htmlDoc = getAsHTML(map);
    +            // check that weird param is correctly encoded to avoid js code execution
    +            assertThat(
    +                    htmlDoc,
    +                    containsString(
    +                            "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'"));
    +            assertThat(htmlDoc, containsString("\"25064;ALERT(1)//419\": '1'"));
    +            assertThat(htmlDoc, not(containsString(layer.getTitle())));
    +            assertThat(htmlDoc, containsString("Title<\\/foo"));
    +            assertThat(htmlDoc, not(containsString(otherStyle.getName())));
    +            assertThat(htmlDoc, containsString("style&lt;&gt;"));
    +        } finally {
    +            request.getLayers().get(0).getLayerInfo().getStyles().remove(otherStyle);
    +        }
         }
     
         @Test
    @@ -472,19 +478,23 @@ public void testXssOL3() throws Exception {
             layer.setTitle("Title");
             map.addLayer(layer);
             request.setFormat("application/openlayers3");
    -        String htmlDoc = getAsHTMLOL3(map);
    -        // check that weird param is correctly encoded to avoid js code execution
    -        int index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf(
    -                                "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'");
    -        assertTrue(index > -1);
    -        index =
    -                htmlDoc.replace("\\n", "")
    -                        .replace("\\r", "")
    -                        .indexOf("\"25064;ALERT(1)//419\": '1'");
    -        assertTrue(index > -1);
    +
    +        StyleInfo otherStyle = new StyleInfoImpl(null);
    +        otherStyle.setName("style<>");
    +        try {
    +            request.getLayers().get(0).getLayerInfo().getStyles().add(otherStyle);
    +            String htmlDoc = getAsHTMLOL3(map);
    +            // check that weird param is correctly encoded to avoid js code execution
    +            assertThat(
    +                    htmlDoc,
    +                    containsString(
    +                            "\"<\\/script><script>alert(\\'x-scripted\\');<\\/script><script>\": 'foo'"));
    +            assertThat(htmlDoc, containsString("\"25064;ALERT(1)//419\": '1'"));
    +            assertThat(htmlDoc, not(containsString(otherStyle.getName())));
    +            assertThat(htmlDoc, containsString("style&lt;&gt;"));
    +        } finally {
    +            request.getLayers().get(0).getLayerInfo().getStyles().remove(otherStyle);
    +        }
         }
     
         @Test
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.