Initial commit
[squeep-logger-json-console] / lib / json-replacers.js
diff --git a/lib/json-replacers.js b/lib/json-replacers.js
new file mode 100644 (file)
index 0000000..2160ad8
--- /dev/null
@@ -0,0 +1,136 @@
+'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,
+};