diff --git a/src/server/config/schema.js b/src/server/config/schema.js index dc960c684189..69b50dc7aaf8 100644 --- a/src/server/config/schema.js +++ b/src/server/config/schema.js @@ -61,7 +61,7 @@ module.exports = Joi.object({ events: Joi.any().default({}), dest: Joi.string().default('stdout'), - + filter: Joi.any().default({}), json: Joi.boolean() .when('dest', { is: 'stdout', diff --git a/src/server/logging/LogFormat.js b/src/server/logging/LogFormat.js index db901d2ecc90..18832199fa91 100644 --- a/src/server/logging/LogFormat.js +++ b/src/server/logging/LogFormat.js @@ -6,6 +6,7 @@ let ansicolors = require('ansicolors'); let stringify = require('json-stringify-safe'); let querystring = require('querystring'); let inspect = require('util').inspect; +let applyFiltersToKeys = require('./applyFiltersToKeys'); function serializeError(err) { return { @@ -24,16 +25,23 @@ let levelColor = function (code) { return ansicolors.red(code); }; + module.exports = class TransformObjStream extends Stream.Transform { - constructor() { + constructor(config) { super({ readableObjectMode: false, writableObjectMode: true }); + this.config = config; + } + + filter(data) { + if (!this.config.filter) return data; + return applyFiltersToKeys(data, this.config.filter); } _transform(event, enc, next) { - var data = this.readEvent(event); + var data = this.filter(this.readEvent(event)); this.push(this.format(data) + '\n'); next(); } diff --git a/src/server/logging/LogReporter.js b/src/server/logging/LogReporter.js index 246e8164db60..ac098732a711 100644 --- a/src/server/logging/LogReporter.js +++ b/src/server/logging/LogReporter.js @@ -8,7 +8,7 @@ let LogFormatString = require('./LogFormatString'); module.exports = class KbnLogger { constructor(events, config) { this.squeeze = new Squeeze(events); - this.format = config.json ? new LogFormatJson() : new LogFormatString(); + this.format = config.json ? new LogFormatJson(config) : new LogFormatString(config); if (config.dest === 'stdout') { this.dest = process.stdout; diff --git a/src/server/logging/__tests__/applyFiltersToKeys.js b/src/server/logging/__tests__/applyFiltersToKeys.js new file mode 100644 index 000000000000..bb900f3fa2fb --- /dev/null +++ b/src/server/logging/__tests__/applyFiltersToKeys.js @@ -0,0 +1,39 @@ +var applyFiltersToKeys = require('../applyFiltersToKeys'); +var expect = require('expect.js'); + +describe('applyFiltersToKeys(obj, actionsByKey)', function () { + it('applies for each key+prop in actionsByKey', function () { + var data = applyFiltersToKeys({ + a: { + b: { + c: 1 + }, + d: { + e: 'foobar' + } + }, + req: { + headers: { + authorization: 'Basic dskd939k2i' + } + } + }, { + b: 'remove', + e: 'censor', + authorization: '/([^\\s]+)$/' + }); + + expect(data).to.eql({ + a: { + d: { + e: 'XXXXXX', + }, + }, + req: { + headers: { + authorization: 'Basic XXXXXXXXXX' + } + } + }); + }); +}); diff --git a/src/server/logging/applyFiltersToKeys.js b/src/server/logging/applyFiltersToKeys.js new file mode 100644 index 000000000000..860bdd38b76f --- /dev/null +++ b/src/server/logging/applyFiltersToKeys.js @@ -0,0 +1,42 @@ +function toPojo(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +function replacer(match, group) { + return (new Array(group.length + 1).join('X')); +} + +function apply(obj, key, action) { + for (let k in obj) { + if (obj.hasOwnProperty(k)) { + let val = obj[k]; + if (k === key) { + if (action === 'remove') { + delete obj[k]; + } + else if (action === 'censor' && typeof val === 'object') { + delete obj[key]; + } + else if (action === 'censor') { + obj[k] = ('' + val).replace(/./g, 'X'); + } + else if (/\/.+\//.test(action)) { + var matches = action.match(/\/(.+)\//); + if (matches) { + let regex = new RegExp(matches[1]); + obj[k] = ('' + val).replace(regex, replacer); + } + } + } else if (typeof val === 'object') { + val = apply(val, key, action); + } + } + } + return obj; +} + +module.exports = function (obj, actionsByKey) { + return Object.keys(actionsByKey).reduce((output, key) => { + return apply(output, key, actionsByKey[key]); + }, toPojo(obj)); +}; diff --git a/src/server/logging/index.js b/src/server/logging/index.js index 8ac4bb4d7ef8..2763baa46208 100644 --- a/src/server/logging/index.js +++ b/src/server/logging/index.js @@ -42,7 +42,14 @@ module.exports = function (kbnServer, server, config) { reporter: require('./LogReporter'), config: { json: config.get('logging.json'), - dest: config.get('logging.dest') + dest: config.get('logging.dest'), + // I'm adding the default here because if you add another filter + // using the commandline it will remove authorization. I want users + // to have to explicitly set --logging.filter.authorization=none to + // have it show up int he logs. + filter: _.defaults(config.get('logging.filter'), { + authorization: 'remove' + }) }, events: _.transform(events, function (filtered, val, key) { // provide a string compatible way to remove events