[Git][debian-gis-team/mapserver][upstream] New upstream version 8.6.5
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Fri Jul 10 18:58:56 BST 2026
Bas Couwenberg pushed to branch upstream at Debian GIS Project / mapserver
Commits:
709e83ca by Bas Couwenberg at 2026-07-10T19:46:42+02:00
New upstream version 8.6.5
- - - - -
18 changed files:
- CITATION.cff
- CMakeLists.txt
- HISTORY.md
- src/flatgeobuf/flatgeobuf_c.cpp
- src/mapimageio.c
- src/mapjoin.c
- src/maplayer.c
- src/mapogcapi.cpp
- src/mapogcfilter.cpp
- src/mapogcfilter.h
- src/mapogcsld.cpp
- src/mappostgresql.c
- src/mapscript/python/pyextend.i
- src/mapserver.h
- src/maptemplate.c
- src/maptree.c
- src/mapwcs.cpp
- src/mapwms.cpp
Changes:
=====================================
CITATION.cff
=====================================
@@ -1,8 +1,8 @@
cff-version: 1.2.0
title: MapServer
message: If you use this software, please cite it using the metadata from this file.
-version: 8.6.4
-date-released: 2026-06-01
+version: 8.6.5
+date-released: 2026-07-10
abstract: MapServer is an Open Source platform for publishing spatial data and interactive mapping applications to the web.
type: software
authors:
=====================================
CMakeLists.txt
=====================================
@@ -17,7 +17,7 @@ include(CheckCSourceCompiles)
set (MapServer_VERSION_MAJOR 8)
set (MapServer_VERSION_MINOR 6)
-set (MapServer_VERSION_REVISION 4)
+set (MapServer_VERSION_REVISION 5)
set (MapServer_VERSION_SUFFIX "")
# Set C++ version
=====================================
HISTORY.md
=====================================
@@ -13,6 +13,21 @@ https://mapserver.org/development/changelog/
The online Migration Guide can be found at https://mapserver.org/MIGRATION_GUIDE.html
+8.6.5 release (2026-07-10)
+--------------------------
+
+- security: OGC API Features: validate offset before rendering to fix reflected XSS (#7573)
+
+- security: WCS POST: NULL-Dereference Denial of Service When `BoundingBox` Omits `crs` (#7556)
+
+- security: Reflected XSS in WMS OpenLayers output via the `X-Forwarded-Host` header (#7544)
+
+- security: Heap buffer overflow in WMS GetLegendGraphic via repeated LAYER parameters (#7555)
+
+- security: msMySQLJoinNext(): prevent SQL injection (#7547)
+
+- security: msPOSTGRESQLJoinNext(): avoid potential SQL injection (#7546)
+
8.6.4 release (2026-06-01)
--------------------------
=====================================
src/flatgeobuf/flatgeobuf_c.cpp
=====================================
@@ -15,15 +15,21 @@ uint8_t FLATGEOBUF_MAGICBYTES_SIZE = sizeof(flatgeobuf_magicbytes);
uint32_t INIT_BUFFER_SIZE = 1024 * 4;
template <typename T>
-void parse_value(uint8_t *data, char **values, uint16_t i, uint32_t &offset, bool found)
+bool parse_value(uint8_t *data, uint32_t size, char **values, uint16_t i, uint32_t &offset, bool found)
{
using std::to_string;
+ if (sizeof(T) > size - offset)
+ {
+ msSetError(MS_FGBERR, "Invalid size for property value", "flatgeobuf_decode_properties");
+ return false;
+ }
if (found)
{
msFree(values[i]);
values[i] = msStrdup(to_string(*((T*) (data + offset))).c_str());
}
offset += sizeof(T);
+ return true;
}
ctx *flatgeobuf_init_ctx()
@@ -139,7 +145,8 @@ int flatgeobuf_decode_feature(ctx *ctx, layerObj *layer, shapeObj *shape)
if (properties && properties->size() != 0) {
ctx->properties = (uint8_t *) properties->data();
ctx->properties_size = properties->size();
- flatgeobuf_decode_properties(ctx, layer, shape);
+ if (flatgeobuf_decode_properties(ctx, layer, shape) == -1)
+ return -1;
} else {
ctx->properties_size = 0;
}
@@ -183,47 +190,51 @@ int flatgeobuf_decode_properties(ctx *ctx, layerObj *layer, shapeObj *shape)
type = column.type;
switch (type) {
case flatgeobuf_column_type_bool:
- parse_value<uint8_t>(data, values, ii, offset, found);
+ if (!parse_value<uint8_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_byte:
- parse_value<int8_t>(data, values, ii, offset, found);
+ if (!parse_value<int8_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_ubyte:
- parse_value<uint8_t>(data, values, ii, offset, found);
+ if (!parse_value<uint8_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_short:
- parse_value<int16_t>(data, values, ii, offset, found);
+ if (!parse_value<int16_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_ushort:
- parse_value<uint16_t>(data, values, ii, offset, found);
+ if (!parse_value<uint16_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_int:
- parse_value<int32_t>(data, values, ii, offset, found);
+ if (!parse_value<int32_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_uint:
- parse_value<uint32_t>(data, values, ii, offset, found);
+ if (!parse_value<uint32_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_long:
- parse_value<int64_t>(data, values, ii, offset, found);
+ if (!parse_value<int64_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_ulong:
- parse_value<uint64_t>(data, values, ii, offset, found);
+ if (!parse_value<uint64_t>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_float:
- parse_value<float>(data, values, ii, offset, found);
+ if (!parse_value<float>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_double:
- parse_value<double>(data, values, ii, offset, found);
+ if (!parse_value<double>(data, size, values, ii, offset, found)) return -1;
break;
case flatgeobuf_column_type_datetime:
case flatgeobuf_column_type_string: {
uint32_t len;
- if (offset + sizeof(len) > size){
+ if (sizeof(len) > size - offset){
msSetError(MS_FGBERR, "Invalid size for string value", "flatgeobuf_decode_properties");
return -1;
}
memcpy(&len, data + offset, sizeof(uint32_t));
offset += sizeof(len);
+ if (len > size - offset){
+ msSetError(MS_FGBERR, "Invalid size for string value", "flatgeobuf_decode_properties");
+ return -1;
+ }
if (found) {
char *str = (char *) msSmallMalloc(len + 1);
memcpy(str, data + offset, len);
=====================================
src/mapimageio.c
=====================================
@@ -860,6 +860,17 @@ static char const *gif_error_msg()
}
}
+/* release the giflib handle, ignoring the close status */
+static void closeGIF(GifFileType *image) {
+#if defined GIFLIB_MAJOR && GIFLIB_MINOR && \
+ ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5))
+ int errcode;
+ DGifCloseFile(image, &errcode);
+#else
+ DGifCloseFile(image);
+#endif
+}
+
/* not fully implemented and tested */
/* missing: set the first pointer to a,r,g,b */
int readGIF(char *path, rasterBufferObj *rb) {
@@ -905,11 +916,24 @@ int readGIF(char *path, rasterBufferObj *rb) {
a = rb->data.rgba.a = &rb->data.rgba.pixels[3];
cmap = (image->Image.ColorMap) ? image->Image.ColorMap : image->SColorMap;
+ if (cmap == NULL) {
+ msSetError(MS_MISCERR, "gif image has no color map", "readGIF()");
+ free(rb->data.rgba.pixels);
+ rb->data.rgba.pixels = NULL;
+ closeGIF(image);
+ return MS_FAILURE;
+ }
+ /* the background index is read straight from the file and may point past
+ the end of the color table */
+ const int bgIndex = (image->SBackGroundColor >= 0 &&
+ image->SBackGroundColor < cmap->ColorCount)
+ ? image->SBackGroundColor
+ : 0;
for (unsigned i = 0; i < rb->width * rb->height; i++) {
*a = 255;
- *r = cmap->Colors[image->SBackGroundColor].Red;
- *g = cmap->Colors[image->SBackGroundColor].Green;
- *b = cmap->Colors[image->SBackGroundColor].Blue;
+ *r = cmap->Colors[bgIndex].Red;
+ *g = cmap->Colors[bgIndex].Green;
+ *b = cmap->Colors[bgIndex].Blue;
a += rb->data.rgba.pixel_step;
r += rb->data.rgba.pixel_step;
g += rb->data.rgba.pixel_step;
@@ -955,6 +979,9 @@ int readGIF(char *path, rasterBufferObj *rb) {
msSetError(MS_MISCERR,
"corrupted gif image, img size exceeds screen size",
"readGIF()");
+ free(rb->data.rgba.pixels);
+ rb->data.rgba.pixels = NULL;
+ closeGIF(image);
return MS_FAILURE;
}
@@ -983,7 +1010,8 @@ int readGIF(char *path, rasterBufferObj *rb) {
for (unsigned j = 0; j < width; j++) {
GifPixelType pix = line[j];
- if (transIdx == -1 || pix != transIdx) {
+ if ((transIdx == -1 || pix != transIdx) &&
+ pix < cmap->ColorCount) {
*a = 255;
*r = cmap->Colors[pix].Red;
*g = cmap->Colors[pix].Green;
@@ -1018,7 +1046,8 @@ int readGIF(char *path, rasterBufferObj *rb) {
}
for (unsigned j = 0; j < width; j++) {
GifPixelType pix = line[j];
- if (transIdx == -1 || pix != transIdx) {
+ if ((transIdx == -1 || pix != transIdx) &&
+ pix < cmap->ColorCount) {
*a = 255;
*r = cmap->Colors[pix].Red;
*g = cmap->Colors[pix].Green;
@@ -1102,25 +1131,7 @@ int readGIF(char *path, rasterBufferObj *rb) {
} while (recordType != TERMINATE_RECORD_TYPE);
-#if defined GIFLIB_MAJOR && GIFLIB_MINOR && \
- ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5))
- if (DGifCloseFile(image, &errcode) == GIF_ERROR) {
- msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
- gif_error_msg(errcode));
- return MS_FAILURE;
- }
-#else
- if (DGifCloseFile(image) == GIF_ERROR) {
-#if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
- msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
- gif_error_msg(image->Error));
-#else
- msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
- gif_error_msg());
-#endif
- return MS_FAILURE;
- }
-#endif
+ closeGIF(image);
return MS_SUCCESS;
}
=====================================
src/mapjoin.c
=====================================
@@ -808,8 +808,22 @@ int msMySQLJoinNext(joinObj *join) {
/* if(strcmp(joininfo->target, msMySQLReadStringAttribute(joininfo->conn, i,
* joininfo->toindex)) == 0) break; */
/* } */
- snprintf(qbuf, sizeof(qbuf), "SELECT * FROM %s WHERE %s = %s", join->table,
- joininfo->tocolumn, joininfo->target);
+
+ char *endptr = NULL;
+ (void)strtoll(joininfo->target, &endptr, 10);
+ if (endptr != joininfo->target + strlen(joininfo->target)) {
+ msSetError(MS_JOINERR,
+ "Non-numeric value in MySQL JOIN colunn not supported.",
+ "msMySQLJoinNext()");
+ return (MS_FAILURE);
+ }
+
+ char *escapedTableName = msReplaceSubstring(join->table, "`", "``");
+ char *escapedToColumn = msReplaceSubstring(joininfo->tocolumn, "`", "``");
+ snprintf(qbuf, sizeof(qbuf), "SELECT * FROM `%s` WHERE `%s` = %s",
+ escapedTableName, escapedToColumn, joininfo->target);
+ msFree(escapedTableName);
+ msFree(escapedToColumn);
MYDEBUG printf("%s<BR>\n", qbuf);
if ((joininfo->qresult =
msMySQLQuery(qbuf, joininfo->conn))) { /* There were some rows found,
=====================================
src/maplayer.c
=====================================
@@ -2254,33 +2254,41 @@ char *LayerDefaultEscapeSQLParam(layerObj *layer, const char *pszString) {
/* */
/* Return the property name in a properly escaped and quoted form. */
/************************************************************************/
-char *LayerDefaultEscapePropertyName(layerObj *layer, const char *pszString) {
+
+char *msDefaultEscapePropertyName(const char *pszString) {
char *pszEscapedStr = NULL;
int i, j = 0;
- if (layer && pszString && strlen(pszString) > 0) {
- int nLength = strlen(pszString);
+ int nLength = strlen(pszString);
- pszEscapedStr = (char *)msSmallMalloc(1 + 2 * nLength + 1 + 1);
- pszEscapedStr[j++] = '"';
+ pszEscapedStr = (char *)msSmallMalloc(1 + 2 * nLength + 1 + 1);
+ pszEscapedStr[j++] = '"';
- for (i = 0; i < nLength; i++) {
- char c = pszString[i];
- if (c == '"') {
- pszEscapedStr[j++] = '"';
- pszEscapedStr[j++] = '"';
- } else if (c == '\\') {
- pszEscapedStr[j++] = '\\';
- pszEscapedStr[j++] = '\\';
- } else
- pszEscapedStr[j++] = c;
- }
- pszEscapedStr[j++] = '"';
- pszEscapedStr[j++] = 0;
+ for (i = 0; i < nLength; i++) {
+ char c = pszString[i];
+ if (c == '"') {
+ pszEscapedStr[j++] = '"';
+ pszEscapedStr[j++] = '"';
+ } else if (c == '\\') {
+ pszEscapedStr[j++] = '\\';
+ pszEscapedStr[j++] = '\\';
+ } else
+ pszEscapedStr[j++] = c;
}
+ pszEscapedStr[j++] = '"';
+ pszEscapedStr[j++] = 0;
+
return pszEscapedStr;
}
+static char *LayerDefaultEscapePropertyName(layerObj *layer,
+ const char *pszString) {
+ if (layer && pszString && strlen(pszString) > 0) {
+ return msDefaultEscapePropertyName(pszString);
+ }
+ return NULL;
+}
+
/*
* msConnectLayer
*
=====================================
src/mapogcapi.cpp
=====================================
@@ -1351,6 +1351,18 @@ static int processCollectionItemsRequest(mapObj *map, cgiRequestObj *request,
return MS_SUCCESS;
}
} else { // bbox query
+ const char *offsetStr = getRequestParameter(request, "offset");
+ if (offsetStr) {
+ if (msStringToInt(offsetStr, &offset, 10) != MS_SUCCESS) {
+ msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Bad value for offset.");
+ return MS_SUCCESS;
+ }
+ if (offset < 0) {
+ msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Offset out of range.");
+ return MS_SUCCESS;
+ }
+ }
+
map->query.type = MS_QUERY_BY_RECT;
map->query.mode = MS_QUERY_MULTIPLE;
map->query.layer = i;
@@ -1376,14 +1388,8 @@ static int processCollectionItemsRequest(mapObj *map, cgiRequestObj *request,
map->query.only_cache_result_count = MS_FALSE;
map->query.maxfeatures = limit;
- const char *offsetStr = getRequestParameter(request, "offset");
if (offsetStr) {
- if (msStringToInt(offsetStr, &offset, 10) != MS_SUCCESS) {
- msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Bad value for offset.");
- return MS_SUCCESS;
- }
-
- if (offset < 0 || offset >= numberMatched) {
+ if (offset >= numberMatched) {
msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Offset out of range.");
return MS_SUCCESS;
}
=====================================
src/mapogcfilter.cpp
=====================================
@@ -806,6 +806,26 @@ int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
return status;
}
+static bool msCheckDepthLessThanInternal(const CPLXMLNode *psNode,
+ int nMaxDepth) {
+ if (nMaxDepth <= 0)
+ return false;
+ for (const CPLXMLNode *psIter = psNode->psChild; psIter;
+ psIter = psIter->psNext) {
+ if (!msCheckDepthLessThanInternal(psIter, nMaxDepth - 1))
+ return false;
+ }
+ return true;
+}
+
+bool msCheckDepthLessThan(const CPLXMLNode *psNode, int nMaxDepth) {
+ for (const CPLXMLNode *psIter = psNode; psIter; psIter = psIter->psNext) {
+ if (!msCheckDepthLessThanInternal(psIter, nMaxDepth))
+ return false;
+ }
+ return true;
+}
+
/************************************************************************/
/* FilterNode *FLTPaserFilterEncoding(char *szXMLString) */
/* */
@@ -828,6 +848,11 @@ FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString) {
if (psRoot == NULL)
return NULL;
+ if (!msCheckDepthLessThan(psRoot, 256)) {
+ msDebug("FLTParseFilterEncoding(): %s", "Too deep nesting in filter");
+ CPLDestroyXMLNode(psRoot);
+ return NULL;
+ }
/* strip namespaces. We srtip all name spaces (#1350)*/
CPLStripXMLNamespace(psRoot, NULL, 1);
=====================================
src/mapogcfilter.h
=====================================
@@ -54,6 +54,9 @@ typedef struct {
/* -------------------------------------------------------------------- */
/* prototypes. */
/* -------------------------------------------------------------------- */
+
+bool msCheckDepthLessThan(const CPLXMLNode *psNode, int nMaxDepth);
+
MS_DLL_EXPORT int FLTIsNumeric(const char *pszValue);
MS_DLL_EXPORT int FLTApplyExpressionToLayer(layerObj *lp,
const char *pszExpression);
=====================================
src/mapogcsld.cpp
=====================================
@@ -644,6 +644,11 @@ layerObj *msSLDParseSLD(mapObj *map, const char *psSLDXML, int *pnLayers) {
msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
return NULL;
}
+ if (!msCheckDepthLessThan(psRoot, 256)) {
+ msSetError(MS_WMSERR, "Invalid SLD document : too deep nesting", "");
+ CPLDestroyXMLNode(psRoot);
+ return NULL;
+ }
/* strip namespaces ogc and sld and gml */
CPLStripXMLNamespace(psRoot, "ogc", 1);
@@ -4981,9 +4986,11 @@ static char *msSLDGetAttributeNameOrValue(const char *pszExpression,
iValue = 0;
for (int i = 0; i < nLength; i++) {
// Check for comparison operator
- if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2)) ||
- (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
- (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2))) {
+ if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
+ (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2)) ||
+ (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2) &&
+ (i == 0 || pszExpression[i - 1] == ' ') &&
+ (i + 2 >= nLength || pszExpression[i + 2] == ' '))) {
// Extract value after the operator
int offset = 2; // All operators are 2 characters long
pszAttributeValue = msStrdup(&pszExpression[i + offset]);
=====================================
src/mappostgresql.c
=====================================
@@ -266,6 +266,19 @@ int msPOSTGRESQLJoinPrepare(joinObj *join, shapeObj *shape) {
return MS_SUCCESS;
}
+static char *msPOSTGRESQLEscapeSQLParam(PGconn *conn, const char *pszString) {
+
+ size_t nSrcLen = strlen(pszString);
+ char *pszEscapedStr = (char *)msSmallMalloc(2 * nSrcLen + 1);
+ int nError = 0;
+ PQescapeStringConn(conn, pszEscapedStr, pszString, nSrcLen, &nError);
+ if (nError != 0) {
+ free(pszEscapedStr);
+ pszEscapedStr = NULL;
+ }
+ return pszEscapedStr;
+}
+
/************************************************************************/
/* msPOSTGRESQLJoinNext() */
/* */
@@ -306,7 +319,8 @@ int msPOSTGRESQLJoinNext(joinObj *join) {
/* Write the list of column names. */
length = 0;
for (i = 0; i < join->numitems; i++) {
- length += 8 + strlen(join->items[i]) + 2;
+ length += strlen("::text") + strlen("\"\"") + strlen(join->items[i]) * 2 +
+ strlen(", ");
}
if (length > 1024 * 1024) {
msSetError(MS_MEMERR, "Too many joins.\n", "msPOSTGRESQLJoinNext()");
@@ -321,24 +335,33 @@ int msPOSTGRESQLJoinNext(joinObj *join) {
columns[0] = 0;
for (i = 0; i < join->numitems; i++) {
- strcat(columns, "\"");
- strcat(columns, join->items[i]);
- strcat(columns, "\"::text");
+ char *stresc = msDefaultEscapePropertyName(join->items[i]);
+ strcat(columns, stresc);
+ msFree(stresc);
+ strcat(columns, "::text");
if (i != join->numitems - 1) {
strcat(columns, ", ");
}
}
/* Create the query string. */
- const size_t nSize = 26 + strlen(columns) + strlen(join->table) +
- strlen(join->to) + strlen(joininfo->from_value);
- sql = (char *)malloc(nSize);
- if (!sql) {
- msSetError(MS_MEMERR, "Failure to malloc.\n", "msPOSTGRESQLJoinNext()");
+ char *pszEscapedFrom =
+ msPOSTGRESQLEscapeSQLParam(joininfo->conn, joininfo->from_value);
+ if (!pszEscapedFrom) {
+ free(columns);
return MS_FAILURE;
}
+ char *pszEscapedTable = msDefaultEscapePropertyName(join->table);
+ char *pszEscapedTo = msDefaultEscapePropertyName(join->to);
+ const size_t nSize = strlen("SELECT FROM WHERE = ''") + strlen(columns) +
+ strlen(pszEscapedTable) + strlen(pszEscapedTo) +
+ strlen(pszEscapedFrom) + 1;
+ sql = (char *)msSmallMalloc(nSize);
snprintf(sql, nSize, "SELECT %s FROM %s WHERE %s = '%s'", columns,
- join->table, join->to, joininfo->from_value);
+ pszEscapedTable, pszEscapedTo, pszEscapedFrom);
+ msFree(pszEscapedTable);
+ msFree(pszEscapedTo);
+ msFree(pszEscapedFrom);
if (joininfo->layer_debug) {
msDebug("msPOSTGRESQLJoinNext(): executing %s.\n", sql);
}
=====================================
src/mapscript/python/pyextend.i
=====================================
@@ -257,7 +257,7 @@ def fromstring(data, mappath=None, configpath=None):
PyObject *order;
order = PyTuple_New(self->numlayers);
for (i = 0; i < self->numlayers; i++) {
- PyTuple_SetItem(order,i,PyInt_FromLong((long)self->layerorder[i]));
+ PyTuple_SetItem(order,i,PyLong_FromLong((long)self->layerorder[i]));
}
return order;
}
@@ -269,7 +269,7 @@ def fromstring(data, mappath=None, configpath=None):
int i;
Py_ssize_t size = PyTuple_Size(order);
for (i = 0; i < size; i++) {
- self->layerorder[i] = (int)PyInt_AsLong(PyTuple_GetItem(order, i));
+ self->layerorder[i] = (int)PyLong_AsLong(PyTuple_GetItem(order, i));
}
return MS_SUCCESS;
}
@@ -281,8 +281,8 @@ def fromstring(data, mappath=None, configpath=None):
{
PyObject* output ;
output = PyTuple_New(2);
- PyTuple_SetItem(output,0,PyInt_FromLong((long)self->width));
- PyTuple_SetItem(output,1,PyInt_FromLong((long)self->height));
+ PyTuple_SetItem(output,0,PyLong_FromLong((long)self->width));
+ PyTuple_SetItem(output,1,PyLong_FromLong((long)self->height));
return output;
}
=====================================
src/mapserver.h
=====================================
@@ -2889,6 +2889,7 @@ MS_DLL_EXPORT void msStringFirstCap(char *string);
MS_DLL_EXPORT int msEncodeChar(const char);
MS_DLL_EXPORT char *msEncodeUrlExcept(const char *, const char);
MS_DLL_EXPORT char *msEncodeUrl(const char *);
+char *msDefaultEscapePropertyName(const char *pszString);
MS_DLL_EXPORT char *msEscapeJSonString(const char *pszString);
MS_DLL_EXPORT char *msEscapeJSonLikeString(const char *pszString, char chQuote);
MS_DLL_EXPORT char *msEncodeHTMLEntities(const char *string);
=====================================
src/maptemplate.c
=====================================
@@ -43,6 +43,7 @@
#include <assert.h>
#include <ctype.h>
+#include <math.h>
static inline void IGUR_sizet(size_t ignored) {
(void)ignored;
@@ -82,11 +83,19 @@ static const char *const olTemplate =
" <script "
"src=\"[openlayers_js_url]\"></script>\n"
" <script>\n"
+ " if (!ol.proj.get('[openlayers_projection]')) {\n"
+ " ol.proj.addProjection(new ol.proj.Projection({ code: "
+ "'[openlayers_projection]' }));\n"
+ " }\n"
" [openlayers_layer]\n"
" const map = new ol.Map({\n"
" layers: [mslayer],\n"
" target: 'map',\n"
- " view: new ol.View()\n"
+ " view: new ol.View({\n"
+ " projection: '[openlayers_projection]',\n"
+ " minResolution: [ol_minresolution],\n"
+ " maxResolution: [ol_maxresolution]\n"
+ " })\n"
" });\n"
" map.getView().fit([[minx], [miny], [maxx], [maxy]], { size: "
"map.getSize() });\n"
@@ -108,11 +117,7 @@ static const char *const olLayerMapServerTag =
" });";
static const char *const olLayerWMSTag =
- "if (!ol.proj.get('[openlayers_projection]')) {\n"
- " ol.proj.addProjection(new ol.proj.Projection({ code : "
- "'[openlayers_projection]' }));\n"
- " }\n"
- " const mslayer = new ol.layer.Image({\n"
+ "const mslayer = new ol.layer.Image({\n"
" source: new ol.source.Image({\n"
" loader: ol.source.wms.createLoader({\n"
" url: '[mapserv_onlineresource]',\n"
@@ -4101,7 +4106,13 @@ static char *processLine(mapservObj *mapserv, const char *instr, FILE *stream,
if (strstr(outstr, "'[mapserv_onlineresource]'")) {
// Fix
// https://github.com/MapServer/MapServer/security/advisories/GHSA-xqj6-vjqr-33vv
+ // The value is inside a single-quoted JS string in a <script> element.
+ // Escaping the quote is not enough: a literal "</script>" in the value
+ // still closes the element. Encode '<' and '>' as JS unicode escapes so
+ // they cannot break out of the script block.
char *pszEscaped = msEscapeJSonLikeString(ol, '\'');
+ pszEscaped = msReplaceSubstring(pszEscaped, "<", "\\u003c");
+ pszEscaped = msReplaceSubstring(pszEscaped, ">", "\\u003e");
const size_t nLen = 1 + strlen(pszEscaped) + 1 + 1;
char *pszEscapedWithSingleQuotes = (char *)msSmallMalloc(nLen);
snprintf(pszEscapedWithSingleQuotes, nLen, "'%s'", pszEscaped);
@@ -5045,6 +5056,16 @@ int msReturnOpenLayersPage(mapservObj *mapserv) {
}
}
+ if (mapserv->Mode == BROWSE) {
+ char *epsgCode = NULL;
+ msOWSGetEPSGProj(&(mapserv->map->projection), NULL, NULL, MS_TRUE,
+ &epsgCode);
+ if (epsgCode) {
+ // Transfers ownership of epsgCode to projection
+ projection = epsgCode;
+ }
+ }
+
if (mapserv->map->outputformat->mimetype &&
*mapserv->map->outputformat->mimetype) {
format = mapserv->map->outputformat->mimetype;
@@ -5075,12 +5096,36 @@ int msReturnOpenLayersPage(mapservObj *mapserv) {
buffer = msReplaceSubstring(buffer, "[openlayers_js_url]", openlayersUrl);
buffer = msReplaceSubstring(buffer, "[openlayers_css_url]", openlayersCssUrl);
buffer = msReplaceSubstring(buffer, "[openlayers_layer]", layer);
- if (projection)
- buffer = msReplaceSubstring(buffer, "[openlayers_projection]", projection);
- if (format)
+ if (!projection) {
+ // if no projection can be found then use the default OL projection
+ projection = msStrdup("EPSG:3857");
+ }
+ buffer = msReplaceSubstring(buffer, "[openlayers_projection]", projection);
+ if (format) {
buffer = msReplaceSubstring(buffer, "[openlayers_format]", format);
- else
+ } else {
buffer = msReplaceSubstring(buffer, "[openlayers_format]", "image/png");
+ }
+
+ double extentWidth = mapserv->map->extent.maxx - mapserv->map->extent.minx;
+ double extentHeight = mapserv->map->extent.maxy - mapserv->map->extent.miny;
+ // assume default tile size and zoom-levels
+ int zoomLevels = 20;
+ double maxResolution = MS_MAX(extentWidth, extentHeight) / 256.0;
+
+ if (maxResolution <= 0) {
+ maxResolution = 1; // fallback to avoid any divide by zero
+ }
+
+ double minResolution = maxResolution / pow(2.0, zoomLevels);
+ char olMinRes[64], olMaxRes[64];
+
+ snprintf(olMaxRes, sizeof(olMaxRes), "%g", maxResolution);
+ snprintf(olMinRes, sizeof(olMinRes), "%g", minResolution);
+
+ buffer = msReplaceSubstring(buffer, "[ol_maxresolution]", olMaxRes);
+ buffer = msReplaceSubstring(buffer, "[ol_minresolution]", olMinRes);
+
msIO_fwrite(buffer, strlen(buffer), 1, stdout);
free(layer);
free(buffer);
=====================================
src/maptree.c
=====================================
@@ -380,7 +380,7 @@ static int treeAddShapeId(treeObj *tree, int id, rectObj rect) {
return (treeNodeAddShapeId(tree->root, id, rect, tree->maxdepth));
}
-static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi,
+static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi, int numshapes,
ms_bitarray status) {
int i;
@@ -395,14 +395,15 @@ static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi,
/* Add the local nodes shapeids to the list. */
/* -------------------------------------------------------------------- */
for (i = 0; i < node->numshapes; i++)
- msSetBit(status, node->ids[i], 1);
+ if (node->ids[i] >= 0 && node->ids[i] < numshapes)
+ msSetBit(status, node->ids[i], 1);
/* -------------------------------------------------------------------- */
/* Recurse to subnodes if they exist. */
/* -------------------------------------------------------------------- */
for (i = 0; i < node->numsubnodes; i++) {
if (node->subnode[i])
- treeCollectShapeIds(node->subnode[i], aoi, status);
+ treeCollectShapeIds(node->subnode[i], aoi, numshapes, status);
}
}
@@ -415,7 +416,7 @@ ms_bitarray msSearchTree(const treeObj *tree, rectObj aoi) {
return (NULL);
}
- treeCollectShapeIds(tree->root, aoi, status);
+ treeCollectShapeIds(tree->root, aoi, tree->numshapes, status);
return (status);
}
@@ -507,11 +508,13 @@ static void searchDiskTreeNode(SHPTreeHandle disktree, rectObj aoi,
if (disktree->needswap) {
for (i = 0; i < numshapes; i++) {
SwapWord(4, &ids[i]);
- msSetBit(status, ids[i], 1);
+ if (ids[i] >= 0 && ids[i] < disktree->nShapes)
+ msSetBit(status, ids[i], 1);
}
} else {
for (i = 0; i < numshapes; i++)
- msSetBit(status, ids[i], 1);
+ if (ids[i] >= 0 && ids[i] < disktree->nShapes)
+ msSetBit(status, ids[i], 1);
}
free(ids);
ids = NULL;
=====================================
src/mapwcs.cpp
=====================================
@@ -356,6 +356,23 @@ void msWCSSetDefaultBandsRangeSetInfo(wcsParamsObj *params,
free(bandlist);
}
+/************************************************************************/
+/* msWCSSetParam */
+/************************************************************************/
+
+static int msWCSSetParam(char **ppszOut, cgiRequestObj *request, int i,
+ const char *pszExpectedParamName) {
+ if (strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0) {
+ /* Keep the first value supplied and ignore later duplicates. This avoids
+ leaking the previously allocated value when the same parameter appears
+ multiple times in a request (consistent with msWFSSetParam()). */
+ if (*ppszOut == NULL)
+ *ppszOut = msStrdup(request->ParamValues[i]);
+ return 1;
+ }
+ return 0;
+}
+
/************************************************************************/
/* msWCSParseRequest() */
/************************************************************************/
@@ -434,6 +451,15 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
if (EQUAL((char *)tmpNode->name, "BoundingBox")) {
xmlNodePtr cornerNode = NULL;
params->crs = (char *)xmlGetProp(tmpNode, BAD_CAST "crs");
+ if (!params->crs) {
+ msSetError(MS_WCSERR, "Required parameter CRS was not supplied.",
+ "msWCSGetCoverage()");
+ msWCSException(map, "MissingParameterValue", "crs",
+ params->version);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return MS_DONE;
+ }
if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS",
8) == 0)
@@ -447,8 +473,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
msSetError(MS_WCSERR,
"Wrong number of arguments for LowerCorner",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue",
- "LowerCorner", params->version);
+ msWCSException(map, "InvalidParameterValue", "LowerCorner",
+ params->version);
+ msFreeCharArray(tokens, n);
+ xmlFree(value);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return MS_DONE;
}
params->bbox.minx = atof(tokens[0]);
params->bbox.miny = atof(tokens[1]);
@@ -462,8 +493,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
msSetError(MS_WCSERR,
"Wrong number of arguments for UpperCorner",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue",
- "UpperCorner", params->version);
+ msWCSException(map, "InvalidParameterValue", "UpperCorner",
+ params->version);
+ msFreeCharArray(tokens, n);
+ xmlFree(value);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return MS_DONE;
}
params->bbox.maxx = atof(tokens[0]);
params->bbox.maxy = atof(tokens[1]);
@@ -493,8 +529,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
msSetError(MS_WCSERR,
"Wrong number of arguments for GridOrigin",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue",
- "GridOffsets", params->version);
+ msWCSException(map, "InvalidParameterValue", "GridOffsets",
+ params->version);
+ msFreeCharArray(tokens, n);
+ xmlFree(value);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return MS_DONE;
}
params->originx = atof(tokens[0]);
params->originy = atof(tokens[1]);
@@ -507,8 +548,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
msSetError(MS_WCSERR,
"Wrong number of arguments for GridOffsets",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue",
- "GridOffsets", params->version);
+ msWCSException(map, "InvalidParameterValue", "GridOffsets",
+ params->version);
+ msFreeCharArray(tokens, n);
+ xmlFree(value);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return MS_DONE;
}
/* take absolute values to convert to positive RESX/RESY style
WCS 1.0 behavior. *but* this does break some possibilities! */
@@ -540,22 +586,20 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
if (request->NumParams > 0) {
for (i = 0; i < request->NumParams; i++) {
- if (strcasecmp(request->ParamNames[i], "VERSION") == 0)
- params->version = msStrdup(request->ParamValues[i]);
- if (strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0)
- params->updatesequence = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "REQUEST") == 0)
- params->request = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "INTERPOLATION") == 0)
- params->interpolation = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "SERVICE") == 0)
- params->service = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "SECTION") == 0) /* 1.0 */
- params->section =
- msStrdup(request->ParamValues[i]); /* TODO: validate value here */
- else if (strcasecmp(request->ParamNames[i], "SECTIONS") == 0) /* 1.1 */
- params->section =
- msStrdup(request->ParamValues[i]); /* TODO: validate value here */
+ if (msWCSSetParam(&(params->version), request, i, "VERSION")) {
+ } else if (msWCSSetParam(&(params->updatesequence), request, i,
+ "UPDATESEQUENCE")) {
+ } else if (msWCSSetParam(&(params->request), request, i, "REQUEST")) {
+ } else if (msWCSSetParam(&(params->interpolation), request, i,
+ "INTERPOLATION")) {
+ } else if (msWCSSetParam(&(params->service), request, i, "SERVICE")) {
+ } else if (msWCSSetParam(&(params->section), request, i,
+ "SECTION")) { /* 1.0 */
+ /* TODO: validate value here */
+ } else if (msWCSSetParam(&(params->section), request, i,
+ "SECTIONS")) { /* 1.1 */
+ /* TODO: validate value here */
+ }
/* GetCoverage parameters. */
else if (strcasecmp(request->ParamNames[i], "BBOX") == 0) {
@@ -583,14 +627,12 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
else if (strcasecmp(request->ParamNames[i], "COVERAGE") == 0)
params->coverages =
CSLAddString(params->coverages, request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "TIME") == 0)
- params->time = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "FORMAT") == 0)
- params->format = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "CRS") == 0)
- params->crs = msStrdup(request->ParamValues[i]);
- else if (strcasecmp(request->ParamNames[i], "RESPONSE_CRS") == 0)
- params->response_crs = msStrdup(request->ParamValues[i]);
+ else if (msWCSSetParam(&(params->time), request, i, "TIME")) {
+ } else if (msWCSSetParam(&(params->format), request, i, "FORMAT")) {
+ } else if (msWCSSetParam(&(params->crs), request, i, "CRS")) {
+ } else if (msWCSSetParam(&(params->response_crs), request, i,
+ "RESPONSE_CRS")) {
+ }
/* WCS 1.1 DescribeCoverage and GetCoverage ... */
else if (strcasecmp(request->ParamNames[i], "IDENTIFIER") == 0 ||
@@ -606,8 +648,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
if (tokens == NULL || n < 5) {
msSetError(MS_WCSERR, "Wrong number of arguments for BOUNDINGBOX.",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue", "boundingbox",
- params->version);
+ msWCSException(map, "InvalidParameterValue", "boundingbox",
+ params->version);
+ msFreeCharArray(tokens, n);
+ return MS_DONE;
}
/* NOTE: WCS 1.1 boundingbox is center of pixel oriented, not edge
@@ -618,6 +662,7 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
params->bbox.maxx = atof(tokens[2]);
params->bbox.maxy = atof(tokens[3]);
+ msFree(params->crs);
params->crs = msStrdup(tokens[4]);
msFreeCharArray(tokens, n);
/* normalize imageCRS urns to simply "imageCRS" */
@@ -630,8 +675,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
if (tokens == NULL || n < 2) {
msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue", "GridOffsets",
- params->version);
+ msWCSException(map, "InvalidParameterValue", "GridOffsets",
+ params->version);
+ msFreeCharArray(tokens, n);
+ return MS_DONE;
}
/* take absolute values to convert to positive RESX/RESY style
WCS 1.0 behavior. *but* this does break some possibilities! */
@@ -643,8 +690,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
if (tokens == NULL || n < 2) {
msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
"msWCSParseRequest()");
- return msWCSException(map, "InvalidParameterValue", "GridOffsets",
- params->version);
+ msWCSException(map, "InvalidParameterValue", "GridOffsets",
+ params->version);
+ msFreeCharArray(tokens, n);
+ return MS_DONE;
}
params->originx = atof(tokens[0]);
params->originy = atof(tokens[1]);
@@ -3163,6 +3212,11 @@ int msWCSDispatch(mapObj *map, cgiRequestObj *request,
strcmp(ows_request->version, "1.1.2") == 0) {
auto paramsTmp = msWCSCreateParams();
status = msWCSParseRequest(request, paramsTmp, map);
+ if (status == MS_DONE) {
+ msWCSFreeParams(paramsTmp);
+ free(paramsTmp);
+ return MS_FAILURE;
+ }
if (status == MS_FAILURE) {
msWCSFreeParams(paramsTmp);
free(paramsTmp);
=====================================
src/mapwms.cpp
=====================================
@@ -991,9 +991,12 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion, char **names,
non mandatory
*/
for (int i = 0; i < numentries; i++) {
+ if (strcasecmp(names[i], "REQUEST") == 0) {
+ request = values[i];
+ }
/* check if SLD is passed. If yes, check for OGR support */
- if (strcasecmp(names[i], "SLD") == 0 ||
- strcasecmp(names[i], "SLD_BODY") == 0) {
+ else if (strcasecmp(names[i], "SLD") == 0 ||
+ strcasecmp(names[i], "SLD_BODY") == 0) {
sldenabled =
msOWSLookupMetadata(&(map->web.metadata), "MO", "sld_enabled");
@@ -1012,15 +1015,15 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion, char **names,
}
}
+ const char *layerKey = request && strcasecmp(request, "GetLegendGraphic") == 0
+ ? "LAYER"
+ : "LAYERS";
+
std::vector<std::string> wmslayers;
for (int i = 0; i < numentries; i++) {
/* getMap parameters */
- if (strcasecmp(names[i], "REQUEST") == 0) {
- request = values[i];
- }
-
- if (strcasecmp(names[i], "LAYERS") == 0) {
+ if (strcasecmp(names[i], layerKey) == 0) {
std::vector<int> layerOrder(map->numlayers);
wmslayers = msStringSplit(values[i], ',');
@@ -5983,16 +5986,6 @@ int msWMSDispatch(mapObj *map, cgiRequestObj *req, owsRequestObj *ows_request,
}
if (found) {
isContentDependentLegend = true;
- /* getLegendGraphic uses LAYER= , we need to create a LAYERS= value that
- * is identical we'll suppose that the client is conformat and hasn't
- * included a LAYERS= parameter in its request */
- for (int i = 0; i < req->NumParams; i++) {
- if (strcasecmp(req->ParamNames[i], "LAYER") == 0) {
- req->ParamNames[req->NumParams] = msStrdup("LAYERS");
- req->ParamValues[req->NumParams] = msStrdup(req->ParamValues[i]);
- req->NumParams++;
- }
- }
} else {
return msWMSLegendGraphic(map, nVersion, req->ParamNames,
req->ParamValues, req->NumParams,
View it on GitLab: https://salsa.debian.org/debian-gis-team/mapserver/-/commit/709e83ca1c12cfdc09303172675afc2ab08e9d45
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/mapserver/-/commit/709e83ca1c12cfdc09303172675afc2ab08e9d45
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260710/c35b6ddf/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list