change replacer function signatures to match that of stringify, detect circular refer...
[squeep-logger-json-console] / lib / json-replacers.js
1 'use strict';
2
3 /**
4 * Here are some replacers for rendering peculiar entities as JSON suitable for logging.
5 */
6
7 const http = require('node:http');
8
9 /**
10 * Convert any Error to a plain Object.
11 * @param {String} _key
12 * @param {*} value
13 * @returns {Object}
14 */
15 function replacerError(_key, value) {
16 if (value instanceof Error) {
17 const newValue = {};
18 Object.getOwnPropertyNames(value).forEach((propertyName) => newValue[propertyName] = value[propertyName]); // eslint-disable-line security/detect-object-injection
19 value = newValue;
20 }
21 return value;
22 }
23
24
25 /**
26 * Convert any BigInt type to a String.
27 * @param {String} _key
28 * @param {*} value
29 * @returns {String}
30 */
31 function replacerBigInt(_key, value) {
32 if (typeof value === 'bigint') {
33 value = value.toString();
34 }
35 return value;
36 }
37
38
39 /**
40 * Convert any IncomingMessage to a subset Object.
41 * Also sanitizes any Authorization header. We do this here rather than
42 * in sanitization stage so that we do not have to rely on a set path to
43 * this object.
44 * @param {String} _key
45 * @param {*} value
46 * @returns {Object}
47 */
48 function replacerHttpIncomingMessage(_key, value) {
49 if (value instanceof http.IncomingMessage) {
50 const newValue = {};
51 [
52 'method',
53 'url',
54 'httpVersion',
55 'headers',
56 'trailers',
57 ].forEach((property) => newValue[property] = value[property]); // eslint-disable-line security/detect-object-injection
58
59 if (newValue.headers && 'authorization' in newValue.headers) {
60 const authHeader = newValue.headers.authorization;
61 const spaceIndex = authHeader.indexOf(' ');
62 // This blurs entire auth string if no space found, because -1 from indexOf.
63 const blurredAuthHeader = authHeader.slice(0, spaceIndex + 1) + '*'.repeat(authHeader.length - (spaceIndex + 1));
64 // New headers object, as we change it.
65 newValue.headers = Object.assign({}, newValue.headers, {
66 authorization: blurredAuthHeader,
67 });
68 }
69 value = newValue;
70 }
71 return value;
72 }
73
74
75 /**
76 * Convert any ServerResponse or OutgoingMessage to a subset Object.
77 * @param {String} _key
78 * @param {*} value
79 * @returns {Object}
80 */
81 function replacerHttpServerResponse(_key, value) {
82 if (value instanceof http.OutgoingMessage || value instanceof http.ServerResponse) {
83 const newValue = {};
84 [
85 'statusCode',
86 'statusMessage',
87 ].forEach((property) => newValue[property] = value[property]); // eslint-disable-line security/detect-object-injection
88 newValue.headers = value.getHeaders();
89 value = newValue;
90 }
91 return value;
92 }
93
94
95 module.exports = {
96 replacerBigInt,
97 replacerError,
98 replacerHttpIncomingMessage,
99 replacerHttpServerResponse,
100 };