--- /dev/null
+'use strict';
+
+/**
+ * Here are some replacers for rendering peculiar entities as JSON suitable for logging.
+ */
+
+const http = require('http');
+
+/**
+ * @typedef {Object} ReplacerResult
+ * @property {Boolean} replaced
+ * @property {*} value
+ */
+
+
+/**
+ * template for replacers
+ * @param {String} _key
+ * @param {*} value
+ * @returns {ReplacerResult}
+ */
+/* istanbul ignore next */
+function _replacer(_key, value) {
+ let replaced = false;
+
+ if (undefined) { // eslint-disable-line no-constant-condition
+ replaced = true;
+
+ const newValue = Object.assign({}, value);
+
+ value = newValue;
+ }
+ return { replaced, value };
+}
+
+
+/**
+ * Convert any Error to a plain Object.
+ * @param {String} _key
+ * @param {*} value
+ * @returns {ReplacerResult}
+ */
+function replacerError(_key, value) {
+ let replaced = false;
+ if (value instanceof Error) {
+ replaced = true;
+ const newValue = {};
+ Object.getOwnPropertyNames(value).forEach((propertyName) => newValue[propertyName] = value[propertyName]); // eslint-disable-line security/detect-object-injection
+ value = newValue;
+ }
+ return { replaced, value };
+}
+
+
+/**
+ * Convert any BigInt type to a String.
+ * @param {String} _key
+ * @param {*} value
+ * @returns {ReplacerResult}
+ */
+function replacerBigInt(_key, value) {
+ let replaced = false;
+ if (typeof value === 'bigint') {
+ replaced = true;
+ value = value.toString();
+ }
+ return { replaced, value };
+}
+
+
+/**
+ * Convert any IncomingMessage to a subset Object.
+ * Also sanitizes any Authorization header. We do this here rather than
+ * in sanitization stage so that we do not have to rely on a set path to
+ * this object.
+ * @param {String} _key
+ * @param {*} value
+ * @returns {ReplacerResult}
+ */
+function replacerHttpIncomingMessage(_key, value) {
+ let replaced = false;
+ if (value instanceof http.IncomingMessage) {
+ replaced = true;
+ const newValue = {};
+ [
+ 'method',
+ 'url',
+ 'httpVersion',
+ 'headers',
+ 'trailers',
+ ].forEach((property) => newValue[property] = value[property]); // eslint-disable-line security/detect-object-injection
+
+ if (newValue.headers && 'authorization' in newValue.headers) {
+ const authHeader = newValue.headers.authorization;
+ const spaceIndex = authHeader.indexOf(' ');
+ // This blurs entire auth string if no space found, because -1 from indexOf.
+ const blurredAuthHeader = authHeader.slice(0, spaceIndex + 1) + '*'.repeat(authHeader.length - (spaceIndex + 1));
+ // New headers object, as we change it.
+ newValue.headers = Object.assign({}, newValue.headers, {
+ authorization: blurredAuthHeader,
+ });
+ }
+ value = newValue;
+ }
+ return { replaced, value };
+}
+
+
+/**
+ * Convert any ServerResponse or OutgoingMessage to a subset Object.
+ * @param {String} _key
+ * @param {*} value
+ * @returns {ReplacerResult}
+ */
+function replacerHttpServerResponse(_key, value) {
+ let replaced = false;
+ if (value instanceof http.OutgoingMessage || value instanceof http.ServerResponse) {
+ replaced = true;
+ const newValue = {};
+ [
+ 'statusCode',
+ 'statusMessage',
+ ].forEach((property) => newValue[property] = value[property]); // eslint-disable-line security/detect-object-injection
+ newValue.headers = value.getHeaders();
+ value = newValue;
+ }
+ return { replaced, value };
+}
+
+
+module.exports = {
+ replacerBigInt,
+ replacerError,
+ replacerHttpIncomingMessage,
+ replacerHttpServerResponse,
+};