From d4d64f925d79acd0abdf2de6d964a16a311a0c83 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sun, 4 Sep 2016 17:46:28 -0600 Subject: [PATCH] * Attempt to hide sensitive information by default (e.g. 'password' fields) in log * New logger configuration - change logging: level -> logging: rotatingFile: level for current functionality --- core/config.js | 12 +++++- core/logger.js | 107 +++++++++++++++++++++++++++---------------------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/core/config.js b/core/config.js index a6bbe25a..b6f92ded 100644 --- a/core/config.js +++ b/core/config.js @@ -292,7 +292,17 @@ function getDefaultConfig() { }, logging : { - level : 'debug' + level : 'debug', + + rotatingFile : { // set to 'disabled' or false to disable + type : 'rotating-file', + fileName : 'enigma-bbs.log', + period : '1d', + count : 3, + level : 'debug', + } + + // :TODO: syslog - https://github.com/mcavage/node-bunyan-syslog }, debug : { diff --git a/core/logger.js b/core/logger.js index 47f465f7..0d71a2fe 100644 --- a/core/logger.js +++ b/core/logger.js @@ -1,63 +1,74 @@ /* jslint node: true */ 'use strict'; -var bunyan = require('bunyan'); -var paths = require('path'); -var fs = require('fs'); +// deps +const bunyan = require('bunyan'); +const paths = require('path'); +const fs = require('fs'); +const _ = require('lodash'); -module.exports = { - init : function() { - var Config = require('./config.js').config; - //var ringBufferLimit = miscUtil.valueWithDefault(config.logRingBufferLimit, 100); - var logPath = Config.paths.logs; +module.exports = class Log { - // - // Create something a bit more friendly if the log directory cannot be used - // - // :TODO: this seems cheesy... - var logPathError; - try { - var pathStat = fs.statSync(logPath); - if(!pathStat.isDirectory()) { - logPathError = logPath + ' is not a directory!'; - } - } catch(e) { - if('ENOENT' === e.code) { - logPathError = 'No such file or directory: ' + logPath; - } else { - logPathError = e.message; - } + static init() { + const Config = require('./config.js').config; + const logPath = Config.paths.logs; + + const err = this.checkLogPath(logPath); + if(err) { + console.error(err.message); // eslint-disable-line no-console + return process.exit(); } - if(logPathError) { - console.error(logPathError); - process.exit(); + const logStreams = []; + if(_.isObject(Config.logging.rotatingFile)) { + Config.logging.rotatingFile.path = paths.join(logPath, Config.logging.rotatingFile.fileName); + logStreams.push(Config.logging.rotatingFile); } - var logFile = paths.join(logPath, 'enigma-bbs.log'); + const serializers = { + err : bunyan.stdSerializers.err, // handle 'err' fields with stack/etc. + }; - // :TODO: make this configurable -- - // user should be able to configure rotations, levels to file vs ringBuffer, - // completely disable logging, etc. + // try to remove sensitive info by default, e.g. 'password' fields + [ 'formData', 'formValue' ].forEach(keyName => { + serializers[keyName] = (fd) => Log.hideSensitive(fd); + }); this.log = bunyan.createLogger({ - name : 'ENiGMA½ BBS', - streams : [ - { - type : 'rotating-file', - path : logFile, - period : Config.logging.period || '1d', - count : 3, - level : Config.logging.level || 'debug', - } - /*, - { - type : 'raw', - stream : ringBuffer, - level : 'trace' - }*/ - ], - serializers: { err : bunyan.stdSerializers.err } // handle 'err' fields with stack/etc. + name : 'ENiGMA½ BBS', + streams : logStreams, + serializers : serializers, }); } + + static checkLogPath(logPath) { + try { + if(!fs.statSync(logPath).isDirectory()) { + return new Error(`${logPath} is not a directory`); + } + + return null; + } catch(e) { + if('ENOENT' === e.code) { + return new Error(`${logPath} does not exist`); + } + return e; + } + } + + static hideSensitive(obj) { + try { + // + // Use a regexp -- we don't know how nested fields we want to seek and destroy may be + // + return JSON.parse( + JSON.stringify(obj).replace(/"(password)"\s?:\s?"([^"]+)"/, (match, valueName) => { + return `"${valueName}":"********"`; + }) + ); + } catch(e) { + // be safe and return empty obj! + return {}; + } + } };