diff --git a/docs/config.rst b/docs/config.rst index 648957f..e61bb1c 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -157,6 +157,13 @@ Allows the rendering of marker icons fetched via http(s) hyperlinks. For security reasons only allow this if you can control the origins from where the markers are fetched! Default is to disallow fetching of icons from remote sources. +``allowInlineMarkerImages`` +-------------- +Allows the rendering of inline marker icons or base64 urls. +For security reasons only allow this if you can control the origins from where the markers are fetched! +Not used by default. + + ``styles`` ========== diff --git a/docs/endpoints.rst b/docs/endpoints.rst index 6dcb5ab..cfed7f0 100644 --- a/docs/endpoints.rst +++ b/docs/endpoints.rst @@ -35,7 +35,7 @@ Static images * All the static image endpoints additionally support following query parameters: - * ``path`` - ``((fill|stroke|width)\:[^\|]+\|)*((enc:.+)|((-?\d+\.?\d*,-?\d+\.?\d*\|)+(-?\d+\.?\d*,-?\d+\.?\d*)))`` + * ``path`` - ``((fill|stroke|width)\:[^\|]+\|)*(enc:.+|-?\d+(\.\d*)?,-?\d+(\.\d*)?(\|-?\d+(\.\d*)?,-?\d+(\.\d*)?)+)`` * comma-separated ``lng,lat``, pipe-separated pairs @@ -50,7 +50,7 @@ Static images * e.g. ``path=stroke:yellow|width:2|fill:green|5.9,45.8|5.9,47.8|10.5,47.8|10.5,45.8|5.9,45.8`` or ``path=stroke:blue|width:1|fill:yellow|enc:_p~iF~ps|U_ulLnnqC_mqNvxq`@`` - * can be provided multiple times + * can be provided multiple times * ``latlng`` - indicates coordinates are in ``lat,lng`` order rather than the usual ``lng,lat`` * ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``) diff --git a/package.json b/package.json index bce471c..7da398b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "chokidar": "3.5.3", "clone": "2.1.2", "color": "4.2.3", - "commander": "11.0.0", + "commander": "11.1.0", "cors": "2.8.5", "express": "4.18.2", "handlebars": "4.7.8", @@ -38,7 +38,7 @@ "morgan": "1.10.0", "pbf": "3.2.1", "pmtiles": "2.11.0", - "proj4": "2.9.0", + "proj4": "2.9.1", "request": "2.88.2", "sanitize-filename": "1.6.3", "sharp": "0.32.6", diff --git a/src/serve_rendered.js b/src/serve_rendered.js index f12ef7e..e4290c7 100644 --- a/src/serve_rendered.js +++ b/src/serve_rendered.js @@ -32,8 +32,8 @@ import { const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)'; const PATH_PATTERN = - /^((fill|stroke|width)\:[^\|]+\|)*((enc:.+)|((-?\d+\.?\d*,-?\d+\.?\d*\|)+(-?\d+\.?\d*,-?\d+\.?\d*)))/; -const httpTester = /^(http(s)?:)?\/\//; + /^((fill|stroke|width)\:[^\|]+\|)*(enc:.+|-?\d+(\.\d*)?,-?\d+(\.\d*)?(\|-?\d+(\.\d*)?,-?\d+(\.\d*)?)+)/; +const httpTester = /^\/\//; const mercator = new SphericalMercator(); const getScale = (scale) => (scale || '@1x').slice(1, 2) | 0; @@ -168,10 +168,7 @@ const extractPathsFromQuery = (query, transformer) => { // Iterate through paths, parse and validate them for (const providedPath of providedPaths) { // Logic for pushing coords to path when path includes google polyline - if ( - providedPath.includes('enc:') && - PATH_PATTERN.test(decodeURIComponent(providedPath)) - ) { + if (providedPath.includes('enc:') && PATH_PATTERN.test(providedPath)) { // +4 because 'enc:' is 4 characters, everything after 'enc:' is considered to be part of the polyline const encIndex = providedPath.indexOf('enc:') + 4; const coords = polyline @@ -289,7 +286,10 @@ const extractMarkersFromQuery = (query, options, transformer) => { let iconURI = markerParts[1]; // Check if icon is served via http otherwise marker icons are expected to // be provided as filepaths relative to configured icon path - if (!(iconURI.startsWith('http://') || iconURI.startsWith('https://'))) { + const isRemoteURL = + iconURI.startsWith('http://') || iconURI.startsWith('https://'); + const isDataURL = iconURI.startsWith('data:'); + if (!(isRemoteURL || isDataURL)) { // Sanitize URI with sanitize-filename // https://www.npmjs.com/package/sanitize-filename#details iconURI = sanitize(iconURI); @@ -302,7 +302,9 @@ const extractMarkersFromQuery = (query, options, transformer) => { iconURI = path.resolve(options.paths.icons, iconURI); // When we encounter a remote icon check if the configuration explicitly allows them. - } else if (options.allowRemoteMarkerIcons !== true) { + } else if (isRemoteURL && options.allowRemoteMarkerIcons !== true) { + continue; + } else if (isDataURL && options.allowInlineMarkerImages !== true) { continue; } @@ -437,7 +439,7 @@ const drawMarkers = async (ctx, markers, z) => { * @param {number} z Map zoom level. */ const drawPath = (ctx, path, query, pathQuery, z) => { - const splitPaths = decodeURIComponent(pathQuery).split('|'); + const splitPaths = pathQuery.split('|'); if (!path || path.length < 2) { return null; diff --git a/src/serve_style.js b/src/serve_style.js index 564e430..efc77de 100644 --- a/src/serve_style.js +++ b/src/serve_style.js @@ -9,7 +9,7 @@ import { validate } from '@maplibre/maplibre-gl-style-spec'; import { getPublicUrl } from './utils.js'; -const httpTester = /^(http(s)?:)?\/\//; +const httpTester = /^\/\//; const fixUrl = (req, url, publicUrl, opt_nokey) => { if (!url || typeof url !== 'string' || url.indexOf('local://') !== 0) { diff --git a/test/static.js b/test/static.js index 302becb..32bd80c 100644 --- a/test/static.js +++ b/test/static.js @@ -180,7 +180,7 @@ describe('Static endpoints', function () { 200, 2, /image\/png/, - '?path=' + decodeURIComponent('enc:{{biGwvyGoUi@s_A|{@'), + '?path=' + encodeURIComponent('enc:{{biGwvyGoUi@s_A|{@'), ); }); });