diff --git a/spec/FileLoggerAdapter.spec.js b/spec/FileLoggerAdapter.spec.js index fb4d6d5572..f259422df6 100644 --- a/spec/FileLoggerAdapter.spec.js +++ b/spec/FileLoggerAdapter.spec.js @@ -1,5 +1,8 @@ +'use strict'; + var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; var Parse = require('parse/node').Parse; +var request = require('request'); describe('info logs', () => { @@ -45,3 +48,55 @@ describe('error logs', () => { }); }); }); + +describe('verbose logs', () => { + + it("mask sensitive information in _User class", (done) => { + let customConfig = Object.assign({}, defaultConfiguration, {verbose: true}); + setServerConfiguration(customConfig); + createTestUser().then(() => { + let fileLoggerAdapter = new FileLoggerAdapter(); + return fileLoggerAdapter.query({ + from: new Date(Date.now() - 500), + size: 100, + level: 'verbose' + }); + }).then((results) => { + expect(results[1].message.includes('"password": "********"')).toEqual(true); + var headers = { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest' + }; + request.get({ + headers: headers, + url: 'http://localhost:8378/1/login?username=test&password=moon-y' + }, (error, response, body) => { + let fileLoggerAdapter = new FileLoggerAdapter(); + return fileLoggerAdapter.query({ + from: new Date(Date.now() - 500), + size: 100, + level: 'verbose' + }).then((results) => { + expect(results[1].message.includes('password=********')).toEqual(true); + done(); + }); + }); + }); + }); + + it("should not mask information in non _User class", (done) => { + let obj = new Parse.Object('users'); + obj.set('password', 'pw'); + obj.save().then(() => { + let fileLoggerAdapter = new FileLoggerAdapter(); + return fileLoggerAdapter.query({ + from: new Date(Date.now() - 500), + size: 100, + level: 'verbose' + }); + }).then((results) => { + expect(results[1].message.includes('"password": "pw"')).toEqual(true); + done(); + }); + }); +}); diff --git a/src/PromiseRouter.js b/src/PromiseRouter.js index 099b2474b4..b159ef0768 100644 --- a/src/PromiseRouter.js +++ b/src/PromiseRouter.js @@ -6,6 +6,7 @@ // components that external developers may be modifying. import express from 'express'; +import url from 'url'; import log from './logger'; export default class PromiseRouter { @@ -154,8 +155,8 @@ export default class PromiseRouter { function makeExpressHandler(promiseHandler) { return function(req, res, next) { try { - log.verbose(req.method, req.originalUrl, req.headers, - JSON.stringify(req.body, null, 2)); + log.verbose(req.method, maskSensitiveUrl(req), req.headers, + JSON.stringify(maskSensitiveBody(req), null, 2)); promiseHandler(req).then((result) => { if (!result.response && !result.location && !result.text) { log.error('the handler did not include a "response" or a "location" field'); @@ -194,3 +195,34 @@ function makeExpressHandler(promiseHandler) { } } } + +function maskSensitiveBody(req) { + let maskBody = Object.assign({}, req.body); + let shouldMaskBody = (req.method === 'POST' && req.originalUrl.endsWith('/users') + && !req.originalUrl.includes('classes')) || + (req.method === 'PUT' && /users\/\w+$/.test(req.originalUrl) + && !req.originalUrl.includes('classes')) || + (req.originalUrl.includes('classes/_User')); + if (shouldMaskBody) { + for (let key of Object.keys(maskBody)) { + if (key == 'password') { + maskBody[key] = '********'; + break; + } + } + } + return maskBody; +} + +function maskSensitiveUrl(req) { + let maskUrl = req.originalUrl.toString(); + let shouldMaskUrl = req.method === 'GET' && req.originalUrl.includes('/login') + && !req.originalUrl.includes('classes'); + if (shouldMaskUrl) { + let password = url.parse(req.originalUrl, true).query.password; + if (password) { + maskUrl = maskUrl.replace('password=' + password, 'password=********') + } + } + return maskUrl; +}