GeoServer Stored Cross-Site Scripting (XSS) vulnerability in Simple SVG Renderer
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.4 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 SVG Output Format when the Simple SVG renderer is enabled. Access to the WMS SVG Format is available to all users by default although data and service security may limit users' ability to trigger the XSS. Versions 2.23.4 and 2.24.1 contain a fix for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.geoserver:gs-wmsMaven | < 2.23.4 | 2.23.4 |
org.geoserver:gs-wmsMaven | >= 2.24.0, < 2.24.1 | 2.24.1 |
Affected products
1Patches
21b1835afbb9c[GEOS-11152] Improve handling special characters in the Simple SVG Renderer (#7299)
2 files changed · +49 −38
src/wms/src/main/java/org/geoserver/wms/svg/StreamingSVGMap.java+4 −2 modified@@ -5,6 +5,8 @@ */ package org.geoserver.wms.svg; +import static org.apache.commons.text.StringEscapeUtils.escapeXml10; + import java.io.IOException; import java.io.OutputStream; import java.util.List; @@ -138,10 +140,10 @@ private void writeLayers() throws IOException { String styleName = layer.getStyle().getName(); - writer.write("<g id=\"" + groupId + "\""); + writer.write("<g id=\"" + escapeXml10(groupId) + "\""); if (!styleName.startsWith("#")) { - writer.write(" class=\"" + styleName + "\""); + writer.write(" class=\"" + escapeXml10(styleName) + "\""); } writer.write(">\n");
src/wms/src/test/java/org/geoserver/wms/svg/SVGMapProducerTest.java+45 −36 modified@@ -6,11 +6,7 @@ package org.geoserver.wms.svg; import java.awt.Color; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.StringReader; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.geoserver.wms.WMSMapContent; @@ -31,13 +27,31 @@ import org.locationtech.jts.geom.Polygon; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; public class SVGMapProducerTest extends WMSTestSupport { - @Test - public void testHeterogeneousGeometry() throws Exception { + private static String EXPECTED_DOC = + "<?xml version=\"1.0\" standalone=\"no\"?>" + + "<svg xmlns=\"http://www.w3.org/2000/svg\"" + + " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + + " stroke=\"green\"" + + " fill=\"none\"" + + " stroke-width=\"0.1%\"" + + " stroke-linecap=\"round\"" + + " stroke-linejoin=\"round\"" + + " width=\"300\"" + + " height=\"300\"" + + " viewBox=\"-250.0 -250.0 500.0 500.0\"" + + " preserveAspectRatio=\"xMidYMid meet\">" + + " <g id=\"LAYER\" class=\"STYLE\">" + + " <use x=\"10\" y=\"-10\" xlink:href=\"#point\"/>" + + " <path d=\"M50 -50l50 -50 \"/>" + + " <path d=\"M0 0l0 -200 200 0 0 200 -200 0 Z\"/>" + + " </g>" + + "</svg>"; + + private void doTestSVGMapProducer(String layer, String style, String expectedDoc) + throws Exception { GeometryFactory gf = new GeometryFactory(); Point point = gf.createPoint(new Coordinate(10, 10)); LineString line = @@ -56,7 +70,7 @@ public void testHeterogeneousGeometry() throws Exception { null); SimpleFeatureTypeBuilder ftb = new SimpleFeatureTypeBuilder(); - ftb.setName("test"); + ftb.setName(layer); ftb.add("geom", Geometry.class); SimpleFeatureType type = ftb.buildFeatureType(); @@ -68,7 +82,7 @@ public void testHeterogeneousGeometry() throws Exception { ds.createSchema(type); ds.addFeatures(f1, f2, f3); - FeatureSource fs = ds.getFeatureSource("test"); + FeatureSource fs = ds.getFeatureSource(layer); final WMSMapContent map = new WMSMapContent(); map.getViewport().setBounds(new ReferencedEnvelope(-250, 250, -250, 250, null)); @@ -78,6 +92,7 @@ public void testHeterogeneousGeometry() throws Exception { map.setTransparent(false); Style basicStyle = getCatalog().getStyleByName("Default").getStyle(); + basicStyle.setName(style); map.addLayer(new FeatureLayer(fs, basicStyle)); SVGStreamingMapOutputFormat producer = new SVGStreamingMapOutputFormat(); @@ -86,33 +101,27 @@ public void testHeterogeneousGeometry() throws Exception { encodeSVG.encode(out); // System.out.println(out.toString()); - String expectedDoc = - "<?xml version=\"1.0\" standalone=\"no\"?>" - + "<svg xmlns=\"http://www.w3.org/2000/svg\" " // - + " xmlns:xlink=\"http://www.w3.org/1999/xlink\" " // - + " stroke=\"green\" " // - + " fill=\"none\" " // - + " stroke-width=\"0.1%\" " // - + " stroke-linecap=\"round\" " // - + " stroke-linejoin=\"round\" " // - + " width=\"300\" " // - + " height=\"300\" " // - + " viewBox=\"-250.0 -250.0 500.0 500.0\" " // - + " preserveAspectRatio=\"xMidYMid meet\"> " // - + " <g id=\"test\" class=\"Default\"> " // - + " <use x=\"10\" y=\"-10\" xlink:href=\"#point\"/> " // - + " <path d=\"M50 -50l50 -50 \"/> " // - + " <path d=\"M0 0l0 -200 200 0 0 200 -200 0 Z\"/> " // - + " </g> " // - + "</svg> "; - - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document expected = builder.parse(new InputSource(new StringReader(expectedDoc))); - Document result = builder.parse(new ByteArrayInputStream(out.toByteArray())); - XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreAttributeOrder(true); - XMLUnit.setIgnoreComments(true); - XMLAssert.assertXMLEqual(expected, result); + XMLAssert.assertXMLEqual(expectedDoc, out.toString()); + } + + @Test + public void testHeterogeneousGeometry() throws Exception { + String layer = "test"; + String style = "Default"; + String expectedDoc = EXPECTED_DOC.replace("LAYER", layer).replace("STYLE", style); + doTestSVGMapProducer(layer, style, expectedDoc); + } + + @Test + public void testEscaping() throws Exception { + String unescapedLayer = "layer\"<>"; + String escapedLayer = "layer"<>"; + String unescapedStyle = "style\"<>"; + String escapedStyle = "style"<>"; + String expectedDoc = + EXPECTED_DOC.replace("LAYER", escapedLayer).replace("STYLE", escapedStyle); + doTestSVGMapProducer(unescapedLayer, unescapedStyle, expectedDoc); } }
9f40265febb5[GEOS-11152] Improve handling special characters in the Simple SVG Renderer
2 files changed · +49 −38
src/wms/src/main/java/org/geoserver/wms/svg/StreamingSVGMap.java+4 −2 modified@@ -5,6 +5,8 @@ */ package org.geoserver.wms.svg; +import static org.apache.commons.text.StringEscapeUtils.escapeXml10; + import java.io.IOException; import java.io.OutputStream; import java.util.List; @@ -138,10 +140,10 @@ private void writeLayers() throws IOException { String styleName = layer.getStyle().getName(); - writer.write("<g id=\"" + groupId + "\""); + writer.write("<g id=\"" + escapeXml10(groupId) + "\""); if (!styleName.startsWith("#")) { - writer.write(" class=\"" + styleName + "\""); + writer.write(" class=\"" + escapeXml10(styleName) + "\""); } writer.write(">\n");
src/wms/src/test/java/org/geoserver/wms/svg/SVGMapProducerTest.java+45 −36 modified@@ -6,11 +6,7 @@ package org.geoserver.wms.svg; import java.awt.Color; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.StringReader; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.geoserver.wms.WMSMapContent; @@ -31,13 +27,31 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; public class SVGMapProducerTest extends WMSTestSupport { - @Test - public void testHeterogeneousGeometry() throws Exception { + private static String EXPECTED_DOC = + "<?xml version=\"1.0\" standalone=\"no\"?>" + + "<svg xmlns=\"http://www.w3.org/2000/svg\"" + + " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + + " stroke=\"green\"" + + " fill=\"none\"" + + " stroke-width=\"0.1%\"" + + " stroke-linecap=\"round\"" + + " stroke-linejoin=\"round\"" + + " width=\"300\"" + + " height=\"300\"" + + " viewBox=\"-250.0 -250.0 500.0 500.0\"" + + " preserveAspectRatio=\"xMidYMid meet\">" + + " <g id=\"LAYER\" class=\"STYLE\">" + + " <use x=\"10\" y=\"-10\" xlink:href=\"#point\"/>" + + " <path d=\"M50 -50l50 -50 \"/>" + + " <path d=\"M0 0l0 -200 200 0 0 200 -200 0 Z\"/>" + + " </g>" + + "</svg>"; + + private void doTestSVGMapProducer(String layer, String style, String expectedDoc) + throws Exception { GeometryFactory gf = new GeometryFactory(); Point point = gf.createPoint(new Coordinate(10, 10)); LineString line = @@ -56,7 +70,7 @@ public void testHeterogeneousGeometry() throws Exception { null); SimpleFeatureTypeBuilder ftb = new SimpleFeatureTypeBuilder(); - ftb.setName("test"); + ftb.setName(layer); ftb.add("geom", Geometry.class); SimpleFeatureType type = ftb.buildFeatureType(); @@ -68,7 +82,7 @@ public void testHeterogeneousGeometry() throws Exception { ds.createSchema(type); ds.addFeatures(f1, f2, f3); - FeatureSource fs = ds.getFeatureSource("test"); + FeatureSource fs = ds.getFeatureSource(layer); final WMSMapContent map = new WMSMapContent(); map.getViewport().setBounds(new ReferencedEnvelope(-250, 250, -250, 250, null)); @@ -78,6 +92,7 @@ public void testHeterogeneousGeometry() throws Exception { map.setTransparent(false); Style basicStyle = getCatalog().getStyleByName("Default").getStyle(); + basicStyle.setName(style); map.addLayer(new FeatureLayer(fs, basicStyle)); SVGStreamingMapOutputFormat producer = new SVGStreamingMapOutputFormat(); @@ -86,33 +101,27 @@ public void testHeterogeneousGeometry() throws Exception { encodeSVG.encode(out); // System.out.println(out.toString()); - String expectedDoc = - "<?xml version=\"1.0\" standalone=\"no\"?>" - + "<svg xmlns=\"http://www.w3.org/2000/svg\" " // - + " xmlns:xlink=\"http://www.w3.org/1999/xlink\" " // - + " stroke=\"green\" " // - + " fill=\"none\" " // - + " stroke-width=\"0.1%\" " // - + " stroke-linecap=\"round\" " // - + " stroke-linejoin=\"round\" " // - + " width=\"300\" " // - + " height=\"300\" " // - + " viewBox=\"-250.0 -250.0 500.0 500.0\" " // - + " preserveAspectRatio=\"xMidYMid meet\"> " // - + " <g id=\"test\" class=\"Default\"> " // - + " <use x=\"10\" y=\"-10\" xlink:href=\"#point\"/> " // - + " <path d=\"M50 -50l50 -50 \"/> " // - + " <path d=\"M0 0l0 -200 200 0 0 200 -200 0 Z\"/> " // - + " </g> " // - + "</svg> "; - - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document expected = builder.parse(new InputSource(new StringReader(expectedDoc))); - Document result = builder.parse(new ByteArrayInputStream(out.toByteArray())); - XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreAttributeOrder(true); - XMLUnit.setIgnoreComments(true); - XMLAssert.assertXMLEqual(expected, result); + XMLAssert.assertXMLEqual(expectedDoc, out.toString()); + } + + @Test + public void testHeterogeneousGeometry() throws Exception { + String layer = "test"; + String style = "Default"; + String expectedDoc = EXPECTED_DOC.replace("LAYER", layer).replace("STYLE", style); + doTestSVGMapProducer(layer, style, expectedDoc); + } + + @Test + public void testEscaping() throws Exception { + String unescapedLayer = "layer\"<>"; + String escapedLayer = "layer"<>"; + String unescapedStyle = "style\"<>"; + String escapedStyle = "style"<>"; + String expectedDoc = + EXPECTED_DOC.replace("LAYER", escapedLayer).replace("STYLE", escapedStyle); + doTestSVGMapProducer(unescapedLayer, unescapedStyle, expectedDoc); } }
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- github.com/advisories/GHSA-fg9v-56hw-g525ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-23642ghsaADVISORY
- github.com/geoserver/geoserver/commit/1b1835afbb9c282d1840786259aeda81c1d22b00ghsax_refsource_MISCWEB
- github.com/geoserver/geoserver/commit/9f40265febb5939f23e2c53930c9c35e93970afeghsax_refsource_MISCWEB
- github.com/geoserver/geoserver/pull/7173ghsax_refsource_MISCWEB
- github.com/geoserver/geoserver/security/advisories/GHSA-fg9v-56hw-g525ghsax_refsource_CONFIRMWEB
- osgeo-org.atlassian.net/browse/GEOS-11152ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.