diff --git a/js/logging.js b/js/logging.js index babf211a7..15a38b30d 100644 --- a/js/logging.js +++ b/js/logging.js @@ -8,10 +8,9 @@ const _ = require('lodash'); const debuglogs = require('./modules/debuglogs'); const Errors = require('./modules/types/errors'); +const Privacy = require('./modules/privacy'); const ipc = electron.ipcRenderer; -const PHONE_REGEX = /\+\d{7,12}(\d{3})/g; -const GROUP_REGEX = /(group\()([^)]+)(\))/g; // Default Bunyan levels: https://github.com/trentm/node-bunyan#levels // To make it easier to visually scan logs, we make all levels the same length @@ -25,20 +24,7 @@ const LEVELS = { 10: 'trace', }; - // Backwards-compatible logging, simple strings and no level (defaulted to INFO) - -function redactPhone(text) { - return text.replace(PHONE_REGEX, '+[REDACTED]$1'); -} - -function redactGroup(text) { - return text.replace( - GROUP_REGEX, - (match, before, id, after) => `${before}[REDACTED]${id.slice(-3)}${after}` - ); -} - function now() { const date = new Date(); return date.toJSON(); @@ -61,8 +47,8 @@ function log(...args) { return item; }); - const toSend = redactAll(str.join(' ')); - ipc.send('log-info', toSend); + const logText = Privacy.redactAll(str.join(' ')); + ipc.send('log-info', logText); } if (window.console) { @@ -96,11 +82,7 @@ function formatLine(entry) { } function format(entries) { - return redactAll(entries.map(formatLine).join('\n'))); -} - -function redactAll(string) { - return Errors.redactSensitivePaths(redactGroup(redactPhone(string))); + return Privacy.redactAll(entries.map(formatLine).join('\n')); } function fetch() { diff --git a/js/modules/privacy.js b/js/modules/privacy.js new file mode 100644 index 000000000..55096daee --- /dev/null +++ b/js/modules/privacy.js @@ -0,0 +1,53 @@ +/* eslint-env node */ + +const Path = require('path'); + +const isString = require('lodash/isString'); +const compose = require('lodash/fp/compose'); + + +const PHONE_NUMBER_PATTERN = /\+\d{7,12}(\d{3})/g; +const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; + +const APP_ROOT_PATH = Path.join(__dirname, '..', '..', '..'); +const APP_ROOT_PATH_PATTERN = new RegExp(APP_ROOT_PATH, 'g'); + +const REDACTION_PLACEHOLDER = '[REDACTED]'; + +// redactPhoneNumbers :: String -> String +exports.redactPhoneNumbers = (text) => { + if (!isString(text)) { + throw new TypeError('`text` must be a string'); + } + + return text.replace(PHONE_NUMBER_PATTERN, `+${REDACTION_PLACEHOLDER}$1`); +}; + +// redactGroupIds :: String -> String +exports.redactGroupIds = (text) => { + if (!isString(text)) { + throw new TypeError('`text` must be a string'); + } + + return text.replace( + GROUP_ID_PATTERN, + (match, before, id, after) => + `${before}${REDACTION_PLACEHOLDER}${id.slice(-3)}${after}` + ); +}; + +// redactSensitivePaths :: String -> String +exports.redactSensitivePaths = (text) => { + if (!isString(text)) { + throw new TypeError('`text` must be a string'); + } + + return text.replace(APP_ROOT_PATH_PATTERN, REDACTION_PLACEHOLDER); +}; + +// redactAll :: String -> String +exports.redactAll = compose( + exports.redactSensitivePaths, + exports.redactGroupIds, + exports.redactPhoneNumbers +); diff --git a/js/modules/types/errors.js b/js/modules/types/errors.js index 43addcb2c..52252fb1f 100644 --- a/js/modules/types/errors.js +++ b/js/modules/types/errors.js @@ -1,26 +1,9 @@ -/* eslint-env node */ - -const Path = require('path'); - const ensureError = require('ensure-error'); -const isString = require('lodash/isString'); - -const APP_ROOT_PATH = Path.join(__dirname, '..', '..', '..'); -const APP_ROOT_PATH_PATTERN = new RegExp(APP_ROOT_PATH, 'g'); +const Privacy = require('../privacy'); // toLogFormat :: Error -> String exports.toLogFormat = (error) => { const normalizedError = ensureError(error); - const stackWithRedactedPaths = exports.redactSensitivePaths(normalizedError.stack); - return stackWithRedactedPaths; -}; - -// redactSensitivePaths :: String -> String -exports.redactSensitivePaths = (logLine) => { - if (!isString(logLine)) { - return logLine; - } - - return logLine.replace(APP_ROOT_PATH_PATTERN, ''); + return Privacy.redactAll(normalizedError.stack); }; diff --git a/test/modules/types/errors_test.js b/test/modules/types/errors_test.js index 74a9f4e51..77dfc82f8 100644 --- a/test/modules/types/errors_test.js +++ b/test/modules/types/errors_test.js @@ -27,7 +27,7 @@ describe('Errors', () => { ); assert.include( formattedStack, - '', + '[REDACTED]', 'Formatted stack has redactions' ); return;