added allowRemoteMarkerIcons configuration option and restricted fetching of remote marker icons only when option is set to true;
asynchronously load all available icons in a settings object on server startup; replaced fs.existsSync() call in serve_rendered when drawing marker icons with a check against available icons settings object;
This commit is contained in:
parent
78ddbf5454
commit
ea4c398f26
4 changed files with 53 additions and 6 deletions
|
|
@ -32,6 +32,7 @@ Example:
|
|||
"serveAllFonts": false,
|
||||
"serveAllStyles": false,
|
||||
"serveStaticMaps": true,
|
||||
"allowRemoteMarkerIcons": true,
|
||||
"tileMargin": 0
|
||||
},
|
||||
"styles": {
|
||||
|
|
@ -142,6 +143,13 @@ Optional string to be rendered into the raster tiles (and static maps) as waterm
|
|||
Can be used for hard-coding attributions etc. (can also be specified per-style).
|
||||
Not used by default.
|
||||
|
||||
``allowRemoteMarkerIcons``
|
||||
--------------
|
||||
|
||||
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.
|
||||
|
||||
``styles``
|
||||
==========
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
"proj4": "2.8.0",
|
||||
"request": "2.88.2",
|
||||
"sharp": "0.31.0",
|
||||
"tileserver-gl-styles": "2.0.0"
|
||||
"tileserver-gl-styles": "2.0.0",
|
||||
"sanitize-filename": "1.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "4.3.6",
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import util from 'util';
|
|||
import zlib from 'zlib';
|
||||
import sharp from 'sharp'; // sharp has to be required before node-canvas. see https://github.com/lovell/sharp/issues/371
|
||||
import pkg from 'canvas';
|
||||
import Image from 'canvas';
|
||||
import clone from 'clone';
|
||||
import Color from 'color';
|
||||
import express from 'express';
|
||||
import sanitize from "sanitize-filename";
|
||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||
import mlgl from '@maplibre/maplibre-gl-native';
|
||||
import MBTiles from '@mapbox/mbtiles';
|
||||
|
|
@ -22,7 +22,7 @@ import {getFontsPbf, getTileUrls, fixTileJSONCenter} from './utils.js';
|
|||
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)';
|
||||
const httpTester = /^(http(s)?:)?\/\//;
|
||||
|
||||
const {createCanvas} = pkg;
|
||||
const {createCanvas, Image} = pkg;
|
||||
const mercator = new SphericalMercator();
|
||||
const getScale = (scale) => (scale || '@1x').slice(1, 2) | 0;
|
||||
|
||||
|
|
@ -231,11 +231,20 @@ const extractMarkersFromQuery = (query, options, transformer) => {
|
|||
// 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://'))) {
|
||||
iconURI = path.resolve(options.paths.icons, iconURI);
|
||||
// Ensure icon exists at provided path
|
||||
if (!fs.existsSync(iconURI)) {
|
||||
// Sanitize URI with sanitize-filename
|
||||
// https://www.npmjs.com/package/sanitize-filename#details
|
||||
iconURI = sanitize(iconURI)
|
||||
|
||||
// If the selected icon is not part of available icons skip it
|
||||
if (!options.paths.availableIcons.includes(iconURI)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure marker location could be parsed
|
||||
|
|
|
|||
|
|
@ -95,6 +95,35 @@ export function server(opts) {
|
|||
checkPath('mbtiles');
|
||||
checkPath('icons');
|
||||
|
||||
/**
|
||||
* Recursively get all files within a directory.
|
||||
* Inspired by https://stackoverflow.com/a/45130990/10133863
|
||||
* @param {String} directory Absolute path to a directory to get files from.
|
||||
*/
|
||||
const getFiles = async (directory) => {
|
||||
// Fetch all entries of the directory and attach type information
|
||||
const dirEntries = await fs.promises.readdir(directory, { withFileTypes: true });
|
||||
|
||||
// Iterate through entries and return the relative file-path to the icon directory if it is not a directory
|
||||
// otherwise initiate a recursive call
|
||||
const files = await Promise.all(dirEntries.map((dirEntry) => {
|
||||
const entryPath = path.resolve(directory, dirEntry.name);
|
||||
return dirEntry.isDirectory() ?
|
||||
getFiles(entryPath) : entryPath.replace(paths.icons + path.sep, "");
|
||||
}));
|
||||
|
||||
// Flatten the list of files to a single array
|
||||
return files.flat();
|
||||
}
|
||||
|
||||
// Load all available icons into a settings object
|
||||
startupPromises.push(new Promise(resolve => {
|
||||
getFiles(paths.icons).then((files) => {
|
||||
paths.availableIcons = files;
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
if (options.dataDecorator) {
|
||||
try {
|
||||
options.dataDecoratorFunc = require(path.resolve(paths.root, options.dataDecorator));
|
||||
|
|
|
|||
Loading…
Reference in a new issue