Signal-Desktop/js/modules/privacy.js

102 lines
2.7 KiB
JavaScript
Raw Normal View History

/* eslint-env node */
const is = require('@sindresorhus/is');
2018-04-06 16:40:26 +00:00
const path = require('path');
2018-04-02 19:08:53 +00:00
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;
2020-03-20 19:01:15 +00:00
const UUID_PATTERN = /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{9}([0-9A-F]{3})/gi;
const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g;
const REDACTION_PLACEHOLDER = '[REDACTED]';
// _redactPath :: Path -> String -> String
2018-04-27 21:25:04 +00:00
exports._redactPath = filePath => {
if (!is.string(filePath)) {
2018-04-11 19:44:52 +00:00
throw new TypeError("'filePath' must be a string");
}
const filePathPattern = exports._pathToRegExp(filePath);
2018-04-27 21:25:04 +00:00
return text => {
if (!is.string(text)) {
2018-04-11 19:44:52 +00:00
throw new TypeError("'text' must be a string");
}
if (!is.regExp(filePathPattern)) {
return text;
}
return text.replace(filePathPattern, REDACTION_PLACEHOLDER);
};
};
// _pathToRegExp :: Path -> Maybe RegExp
2018-04-27 21:25:04 +00:00
exports._pathToRegExp = filePath => {
2018-03-07 15:57:39 +00:00
try {
const pathWithNormalizedSlashes = filePath.replace(/\//g, '\\');
2018-04-06 18:25:55 +00:00
const pathWithEscapedSlashes = filePath.replace(/\\/g, '\\\\');
const urlEncodedPath = encodeURI(filePath);
2018-03-07 15:57:39 +00:00
// Safe `String::replaceAll`:
// https://github.com/lodash/lodash/issues/1084#issuecomment-86698786
const patternString = [
filePath,
pathWithNormalizedSlashes,
2018-04-06 18:25:55 +00:00
pathWithEscapedSlashes,
urlEncodedPath,
2018-04-27 21:25:04 +00:00
]
.map(escapeRegExp)
.join('|');
return new RegExp(patternString, 'g');
2018-03-07 15:57:39 +00:00
} catch (error) {
return null;
}
};
// Public API
// redactPhoneNumbers :: String -> String
2018-04-27 21:25:04 +00:00
exports.redactPhoneNumbers = text => {
if (!is.string(text)) {
2018-04-11 19:44:52 +00:00
throw new TypeError("'text' must be a string");
}
return text.replace(PHONE_NUMBER_PATTERN, `+${REDACTION_PLACEHOLDER}$1`);
};
// redactUuids :: String -> String
exports.redactUuids = text => {
if (!is.string(text)) {
throw new TypeError("'text' must be a string");
}
return text.replace(UUID_PATTERN, `${REDACTION_PLACEHOLDER}$1`);
};
// redactGroupIds :: String -> String
2018-04-27 21:25:04 +00:00
exports.redactGroupIds = text => {
if (!is.string(text)) {
2018-04-11 19:44:52 +00:00
throw new TypeError("'text' must be a string");
}
return text.replace(
GROUP_ID_PATTERN,
(match, before, id, after) =>
2018-05-03 15:46:21 +00:00
`${before}${REDACTION_PLACEHOLDER}${removeNewlines(id).slice(-3)}${after}`
);
};
// redactSensitivePaths :: String -> String
exports.redactSensitivePaths = exports._redactPath(APP_ROOT_PATH);
// redactAll :: String -> String
exports.redactAll = compose(
exports.redactSensitivePaths,
exports.redactGroupIds,
exports.redactPhoneNumbers,
exports.redactUuids
);
2018-05-03 15:46:21 +00:00
const removeNewlines = text => text.replace(/\r?\n|\r/g, '');