From b0da7d965e50800759c4ea37a70cae5624613bba Mon Sep 17 00:00:00 2001 From: Daniel Gasienica Date: Fri, 6 Apr 2018 12:54:29 -0400 Subject: [PATCH] Redact URL encoded file paths in stack traces --- js/modules/privacy.js | 48 +++++++++++++++++++++++------------- test/modules/privacy_test.js | 23 ++++++++++++++++- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/js/modules/privacy.js b/js/modules/privacy.js index d85509a44..d1f6ac1ce 100644 --- a/js/modules/privacy.js +++ b/js/modules/privacy.js @@ -7,22 +7,46 @@ const { compose } = require('lodash/fp'); const { escapeRegExp } = require('lodash'); +const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); const PHONE_NUMBER_PATTERN = /\+\d{7,12}(\d{3})/g; const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; +const REDACTION_PLACEHOLDER = '[REDACTED]'; -const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); -const APP_ROOT_PATH_PATTERN = (() => { + +// _redactPath :: Path -> String -> String +exports._redactPath = (filePath) => { + if (!is.string(filePath)) { + throw new TypeError('"filePath" must be a string'); + } + + const filePathPattern = exports._pathToRegExp(filePath); + + return (text) => { + if (!is.string(text)) { + throw new TypeError('"text" must be a string'); + } + + if (!is.regExp(filePathPattern)) { + return text; + } + + return text.replace(filePathPattern, REDACTION_PLACEHOLDER); + }; +}; + +// _pathToRegExp :: Path -> Maybe RegExp +exports._pathToRegExp = (filePath) => { try { // Safe `String::replaceAll`: // https://github.com/lodash/lodash/issues/1084#issuecomment-86698786 - return new RegExp(escapeRegExp(APP_ROOT_PATH), 'g'); + const urlEncodedAppRootPath = escapeRegExp(encodeURI(filePath)); + return new RegExp(`${escapeRegExp(filePath)}|${urlEncodedAppRootPath}`, 'g'); } catch (error) { return null; } -})(); - -const REDACTION_PLACEHOLDER = '[REDACTED]'; +}; +// Public API // redactPhoneNumbers :: String -> String exports.redactPhoneNumbers = (text) => { if (!is.string(text)) { @@ -46,17 +70,7 @@ exports.redactGroupIds = (text) => { }; // redactSensitivePaths :: String -> String -exports.redactSensitivePaths = (text) => { - if (!is.string(text)) { - throw new TypeError('"text" must be a string'); - } - - if (!is.regExp(APP_ROOT_PATH_PATTERN)) { - return text; - } - - return text.replace(APP_ROOT_PATH_PATTERN, REDACTION_PLACEHOLDER); -}; +exports.redactSensitivePaths = exports._redactPath(APP_ROOT_PATH); // redactAll :: String -> String exports.redactAll = compose( diff --git a/test/modules/privacy_test.js b/test/modules/privacy_test.js index 43f317f9a..c7f849285 100644 --- a/test/modules/privacy_test.js +++ b/test/modules/privacy_test.js @@ -34,11 +34,12 @@ describe('Privacy', () => { describe('redactAll', () => { it('should redact all sensitive information', () => { + const encodedAppRootPath = APP_ROOT_PATH.replace(/ /g, '%20'); const text = 'This is a log line with sensitive information:\n' + `path1 ${APP_ROOT_PATH}/main.js\n` + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + - `path2 file:///${APP_ROOT_PATH}/js/background.js.` + + `path2 file:///${encodedAppRootPath}/js/background.js.` + 'phone2 +13334445566 lorem\n' + 'group2 group(abcdefghij) doloret\n'; @@ -53,4 +54,24 @@ describe('Privacy', () => { assert.equal(actual, expected); }); }); + + describe('_redactPath', () => { + it('should redact URL-encoded paths', () => { + const testPath = '/Users/meow/Library/Application Support/Signal Beta'; + const encodedTestPath = encodeURI(testPath); + const text = 'This is a log line with sensitive information:\n' + + `path1 ${testPath}/main.js\n` + + 'phone1 +12223334455 ipsum\n' + + 'group1 group(123456789) doloret\n' + + `path2 file:///${encodedTestPath}/js/background.js.`; + + const actual = Privacy._redactPath(testPath)(text); + const expected = 'This is a log line with sensitive information:\n' + + 'path1 [REDACTED]/main.js\n' + + 'phone1 +12223334455 ipsum\n' + + 'group1 group(123456789) doloret\n' + + 'path2 file:///[REDACTED]/js/background.js.'; + assert.equal(actual, expected); + }); + }); });