UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

85 lines (83 loc) 3.26 kB
"use strict"; /* * Copyright 2018 Teppo Kurki <teppo.kurki@iki.fi> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const moment = require('moment'); const path = require('path'); const { getFullLogDir, listLogFiles } = require('@signalk/streams/logging'); const constants_1 = require("../constants"); module.exports = function (app) { return { start: function () { mountApi(app); }, stop: () => undefined }; }; function mountApi(app) { app.securityStrategy.addAdminMiddleware(`${constants_1.SERVERROUTESPREFIX}/logfiles/`); app.get(`${constants_1.SERVERROUTESPREFIX}/logfiles/`, function (req, res) { listLogFiles(app, (err, files) => { if (err) { console.error(err); res.status(500); res.json('Error reading logfiles list'); return; } res.json(files); }); }); app.get(`${constants_1.SERVERROUTESPREFIX}/logfiles/:filename`, function (req, res) { // Decode URL-encoded characters first, then sanitize let filename = req.params.filename; try { filename = decodeURIComponent(filename); } catch (_e) { // Invalid encoding res.status(400).send('Invalid filename'); return; } // Only allow simple filenames: alphanumeric, dots, hyphens, underscores // This prevents all path traversal attempts including encoded ones if (!/^[a-zA-Z0-9._-]+$/.test(filename)) { res.status(400).send('Invalid filename'); return; } const logDir = getFullLogDir(app); const requestedPath = path.join(logDir, filename); // Verify the resolved path is still within the log directory const resolvedPath = path.resolve(requestedPath); const resolvedLogDir = path.resolve(logDir); if (!resolvedPath.startsWith(resolvedLogDir + path.sep)) { res.status(400).send('Invalid filename'); return; } res.sendFile(resolvedPath); }); app.get(`${constants_1.SERVERROUTESPREFIX}/ziplogs`, function (req, res) { const boatName = app.config.vesselName ? app.config.vesselName : app.config.vesselMMSI ? app.config.vesselMMSI : ''; const sanitizedBoatName = boatName.replace(/\W/g, '_'); const zipFileName = `sk-logs-${sanitizedBoatName}-${moment().format('YYYY-MM-DD-HH-mm')}`; res.zip({ files: [{ path: getFullLogDir(app), name: zipFileName }], filename: zipFileName + '.zip' }); }); }