@itentialopensource/adapter-utils 4.49.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,9 @@
4
4
  /* global adapters brokers g_redis log */
5
5
  /* eslint class-methods-use-this:warn */
6
6
  /* eslint consistent-return:warn */
7
+ /* eslint no-promise-executor-return:warn */
7
8
  /* eslint import/no-dynamic-require:warn */
9
+ /* eslint camelcase:warn */
8
10
  /* eslint no-underscore-dangle: [2, { "allow": ["_id"] }] */
9
11
  /* eslint no-unused-vars:warn */
10
12
  /* eslint no-use-before-define:warn */
@@ -368,10 +370,17 @@ function returnStub(request, entitySchema, callProperties) {
368
370
  const reqBody = request.body;
369
371
  const reqPath = request.header.path;
370
372
 
371
- // these logs are very useful when debugging - however there is the potential for credentials to be exposed.
373
+ // these logs are very useful when debugging - however had to change so we do not log credentials
372
374
  if (authLogging) {
373
- log.debug(`FULL STUB REQUEST: ${JSON.stringify(request.header)}`);
374
- log.debug(`FULL STUB BODY: ${request.body}`);
375
+ // Can only mask values if header is an object - so can not log anything else
376
+ if (request.header) {
377
+ log.debug(`FULL STUB REQUEST: ${JSON.stringify(propUtilInst.scrubSensitiveInfo(request.header))}`);
378
+ }
379
+
380
+ // Can only mask values if body is an object - so can not log anything else
381
+ if (request.body) {
382
+ log.debug(`FULL STUB BODY: ${JSON.stringify(propUtilInst.scrubSensitiveInfo(request.body))}`);
383
+ }
375
384
  }
376
385
 
377
386
  const callResp = {
@@ -744,8 +753,8 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
744
753
 
745
754
  // these logs are very useful when debugging - however there is the potential for credentials to be exposed.
746
755
  if (authLogging) {
747
- log.debug(`FULL REQUEST: ${JSON.stringify(request.header)}`);
748
- log.debug(`FULL BODY: ${request.body}`);
756
+ log.debug(`FULL REQUEST: ${JSON.stringify(propUtilInst.scrubSensitiveInfo(request.header))}`);
757
+ log.debug(`FULL BODY: ${JSON.stringify(propUtilInst.scrubSensitiveInfo(request.body))}`);
749
758
  }
750
759
 
751
760
  // make the call to System
@@ -867,7 +876,7 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
867
876
  healthy = true;
868
877
  doneH2(true);
869
878
  }, (retH2) => {
870
- log.debug(`${origin}: CALL RETURN ${JSON.stringify(callResp)}`);
879
+ log.debug(`${origin}: CALL RETURN ${JSON.stringify(propUtilInst.scrubSensitiveInfo(callResp))}`);
871
880
  useProt = undefined;
872
881
  callResp.reqHdr = request.header.headers;
873
882
  return callback(callResp);
@@ -1203,7 +1212,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
1203
1212
  return resolve({ token: 'faketoken', tokenp2: 'faketoken' });
1204
1213
  }
1205
1214
 
1206
- log.debug(`${origin}: OPTIONS: ${JSON.stringify(options)}`);
1215
+ log.debug(`${origin}: OPTIONS: ${JSON.stringify(propUtilInst.scrubSensitiveInfo(options))}`);
1207
1216
 
1208
1217
  // request the token
1209
1218
  return makeRequest(request, tokenSchema, callProperties, null, 0, (result, merror) => {
@@ -1728,10 +1737,16 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
1728
1737
  tokenSchema.sso.protocol = sso.protocol;
1729
1738
  }
1730
1739
  }
1731
- if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host) {
1740
+ if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host && (sso == null || sso.host === '') && entity === 'getToken') {
1741
+ options.hostname = tokenSchema.sso.host;
1742
+ }
1743
+ if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port && (sso == null || sso.port === '') && entity === 'getToken') {
1744
+ options.port = tokenSchema.sso.port;
1745
+ }
1746
+ if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host && entity !== 'getToken') {
1732
1747
  options.hostname = tokenSchema.sso.host;
1733
1748
  }
1734
- if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port) {
1749
+ if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port && entity !== 'getToken') {
1735
1750
  options.port = tokenSchema.sso.port;
1736
1751
  }
1737
1752
 
@@ -4403,10 +4418,10 @@ class ConnectorRest {
4403
4418
  log.info(`${origin}: Connector SSL connections enabled`);
4404
4419
  }
4405
4420
 
4406
- log.debug(`${origin}: HEALTHCHECK OPTIONS: ${JSON.stringify(options)}`);
4421
+ log.debug(`${origin}: HEALTHCHECK OPTIONS: ${JSON.stringify(this.propUtil.scrubSensitiveInfo(options))}`);
4407
4422
 
4408
4423
  if (payload !== undefined && payload !== null && payload !== '') {
4409
- log.debug(`${origin}: REQUEST: ${payload}`);
4424
+ log.debug(`${origin}: REQUEST: ${JSON.stringify(this.propUtil.scrubSensitiveInfo(payload))}`);
4410
4425
 
4411
4426
  // save it in memory
4412
4427
  cacheHPay = payload;
@@ -4598,10 +4613,10 @@ class ConnectorRest {
4598
4613
  log.info(`${origin}: Connector SSL connections enabled`);
4599
4614
  }
4600
4615
 
4601
- log.debug(`${origin}: OPTIONS: ${JSON.stringify(options)}`);
4616
+ log.debug(`${origin}: OPTIONS: ${JSON.stringify(this.propUtil.scrubSensitiveInfo(options))}`);
4602
4617
 
4603
4618
  if (incoming.body !== undefined && incoming.body !== null && incoming.body !== '') {
4604
- log.debug(`${origin}:REQUEST: ${incoming.body}`);
4619
+ log.debug(`${origin}:REQUEST: ${JSON.stringify(this.propUtil.scrubSensitiveInfo(incoming.body))}`);
4605
4620
  }
4606
4621
 
4607
4622
  const request = {
package/lib/dbUtil.js CHANGED
@@ -595,7 +595,6 @@ class DBUtil {
595
595
  */
596
596
  determineStorage(dbInfo, callback) {
597
597
  const origin = `${this.myid}-dbUtil-determineStorage`;
598
-
599
598
  if (dbInfo) {
600
599
  // priority 1 - use the dbInfo passed in
601
600
  if (dbInfo.dburl && dbInfo.database) {
@@ -1208,7 +1207,6 @@ class DBUtil {
1208
1207
  find(collectionName, options, dbInfo, fsWrite, callback) {
1209
1208
  const origin = `${this.myid}-dbUtil-find`;
1210
1209
  log.trace(origin);
1211
-
1212
1210
  try {
1213
1211
  // verify the required data has been provided
1214
1212
  if (!collectionName) {
@@ -1272,7 +1270,6 @@ class DBUtil {
1272
1270
  log.debug(`${origin}: Data retrieved from file storage`);
1273
1271
  return callback(null, toReturn);
1274
1272
  }
1275
-
1276
1273
  // if using MongoDB storage
1277
1274
  if (storage === Storage.DBINFO || storage === Storage.ADAPTERDB) {
1278
1275
  // Find the data in the database
@@ -0,0 +1,280 @@
1
+ /* @copyright Itential, LLC 2023 */
2
+
3
+ // Set globals
4
+ /* global log */
5
+
6
+ /* NodeJS internal utilities */
7
+
8
+ class GenericHandler {
9
+ /**
10
+ * Adapter Generic Handler
11
+ * @constructor
12
+ */
13
+ constructor(prongId, properties, reqH) {
14
+ this.myid = prongId;
15
+ this.allProps = properties;
16
+ this.requestHandlerInst = reqH;
17
+
18
+ // set up the properties I care about
19
+ this.refreshProperties(properties);
20
+ }
21
+
22
+ /**
23
+ * refreshProperties is used to set up all of the properties for the broker handler.
24
+ * It allows properties to be changed later by simply calling refreshProperties rather
25
+ * than having to restart the broker handler.
26
+ *
27
+ * @function refreshProperties
28
+ * @param {Object} properties - an object containing all of the properties
29
+ */
30
+ refreshProperties(properties) {
31
+ const origin = `${this.myid}-genericHandler-refreshProperties`;
32
+ log.trace(origin);
33
+
34
+ if (!properties) {
35
+ log.error(`${origin}: Generic Handler received no properties!`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Makes the requested generic call
41
+ *
42
+ * @function expandedGenericAdapterRequest
43
+ * @param {Object} metadata - metadata for the call (optional).
44
+ * Can be a stringified Object.
45
+ * @param {String} uriPath - the path of the api call - do not include the host, port, base path or version (optional)
46
+ * @param {String} restMethod - the rest method (GET, POST, PUT, PATCH, DELETE) (optional)
47
+ * @param {Object} pathVars - the parameters to be put within the url path (optional).
48
+ * Can be a stringified Object.
49
+ * @param {Object} queryData - the parameters to be put on the url (optional).
50
+ * Can be a stringified Object.
51
+ * @param {Object} requestBody - the body to add to the request (optional).
52
+ * Can be a stringified Object.
53
+ * @param {Object} addlHeaders - additional headers to be put on the call (optional).
54
+ * Can be a stringified Object.
55
+ * @param {getCallback} callback - a callback function to return the result (Generics)
56
+ * or the error
57
+ */
58
+ expandedGenericAdapterRequest(metadata, uriPath, restMethod, pathVars, queryData, requestBody, addlHeaders, callback) {
59
+ const meth = 'genericHandler-expandedGenericAdapterRequest';
60
+ const origin = `${this.myid}-${meth}`;
61
+ log.trace(origin);
62
+
63
+ // if metadata says not to use BasePath
64
+ if (metadata && metadata.basepath && metadata.basepath.toUpperCase() === 'NOBASE') {
65
+ this.genericAdapterRequestNoBasePath(uriPath, restMethod, queryData, requestBody, addlHeaders, callback);
66
+ }
67
+ // use BasePath
68
+ this.genericAdapterRequest(uriPath, restMethod, queryData, requestBody, addlHeaders, callback);
69
+ }
70
+
71
+ /**
72
+ * Makes the requested generic call
73
+ *
74
+ * @function genericAdapterRequest
75
+ * @param {String} uriPath - the path of the api call - do not include the host, port, base path or version (required)
76
+ * @param {String} restMethod - the rest method (GET, POST, PUT, PATCH, DELETE) (required)
77
+ * @param {Object} queryData - the parameters to be put on the url (optional).
78
+ * Can be a stringified Object.
79
+ * @param {Object} requestBody - the body to add to the request (optional).
80
+ * Can be a stringified Object.
81
+ * @param {Object} addlHeaders - additional headers to be put on the call (optional).
82
+ * Can be a stringified Object.
83
+ * @param {getCallback} callback - a callback function to return the result (Generics)
84
+ * or the error
85
+ */
86
+ genericAdapterRequest(uriPath, restMethod, queryData, requestBody, addlHeaders, callback) {
87
+ const meth = 'genericHandler-genericAdapterRequest';
88
+ const origin = `${this.myid}-${meth}`;
89
+ log.trace(origin);
90
+
91
+ /* HERE IS WHERE YOU VALIDATE DATA */
92
+ if (uriPath === undefined || uriPath === null || uriPath === '') {
93
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['uriPath'], null, null, null);
94
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
95
+ return callback(null, errorObj);
96
+ }
97
+ if (restMethod === undefined || restMethod === null || restMethod === '') {
98
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['restMethod'], null, null, null);
99
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
100
+ return callback(null, errorObj);
101
+ }
102
+
103
+ /* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
104
+ // remove any leading / and split the uripath into path variables
105
+ let myPath = uriPath;
106
+ while (myPath.indexOf('/') === 0) {
107
+ myPath = myPath.substring(1);
108
+ }
109
+ const pathVars = myPath.split('/');
110
+ const queryParamsAvailable = queryData;
111
+ const queryParams = {};
112
+ const bodyVars = requestBody;
113
+
114
+ // loop in template. long callback arg name to avoid identifier conflicts
115
+ Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
116
+ if (queryParamsAvailable[thisKeyInQueryParamsAvailable] !== undefined && queryParamsAvailable[thisKeyInQueryParamsAvailable] !== null
117
+ && queryParamsAvailable[thisKeyInQueryParamsAvailable] !== '') {
118
+ queryParams[thisKeyInQueryParamsAvailable] = queryParamsAvailable[thisKeyInQueryParamsAvailable];
119
+ }
120
+ });
121
+
122
+ // set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders
123
+ const reqObj = {
124
+ payload: bodyVars,
125
+ uriPathVars: pathVars,
126
+ uriQuery: queryParams,
127
+ uriOptions: {}
128
+ };
129
+ // add headers if provided
130
+ if (addlHeaders) {
131
+ reqObj.addlHeaders = addlHeaders;
132
+ }
133
+
134
+ // determine the call and return flag
135
+ let action = 'getGenerics';
136
+ let returnF = true;
137
+ if (restMethod.toUpperCase() === 'POST') {
138
+ action = 'createGeneric';
139
+ } else if (restMethod.toUpperCase() === 'PUT') {
140
+ action = 'updateGeneric';
141
+ } else if (restMethod.toUpperCase() === 'PATCH') {
142
+ action = 'patchGeneric';
143
+ } else if (restMethod.toUpperCase() === 'DELETE') {
144
+ action = 'deleteGeneric';
145
+ returnF = false;
146
+ }
147
+
148
+ try {
149
+ // Make the call -
150
+ // identifyRequest(entity, action, requestObj, returnDataFlag, callback)
151
+ return this.requestHandlerInst.identifyRequest('.generic', action, reqObj, returnF, (irReturnData, irReturnError) => {
152
+ // if we received an error or their is no response on the results
153
+ // return an error
154
+ if (irReturnError) {
155
+ /* HERE IS WHERE YOU CAN ALTER THE ERROR MESSAGE */
156
+ return callback(null, irReturnError);
157
+ }
158
+ if (!Object.hasOwnProperty.call(irReturnData, 'response')) {
159
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Invalid Response', ['genericAdapterRequest'], null, null, null);
160
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
161
+ return callback(null, errorObj);
162
+ }
163
+
164
+ /* HERE IS WHERE YOU CAN ALTER THE RETURN DATA */
165
+ // return the response
166
+ return callback(irReturnData, null);
167
+ });
168
+ } catch (ex) {
169
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
170
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
171
+ return callback(null, errorObj);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Makes the requested generic call with no base path or version
177
+ *
178
+ * @function genericAdapterRequestNoBasePath
179
+ * @param {String} uriPath - the path of the api call - do not include the host, port, base path or version (required)
180
+ * @param {String} restMethod - the rest method (GET, POST, PUT, PATCH, DELETE) (required)
181
+ * @param {Object} queryData - the parameters to be put on the url (optional).
182
+ * Can be a stringified Object.
183
+ * @param {Object} requestBody - the body to add to the request (optional).
184
+ * Can be a stringified Object.
185
+ * @param {Object} addlHeaders - additional headers to be put on the call (optional).
186
+ * Can be a stringified Object.
187
+ * @param {getCallback} callback - a callback function to return the result (Generics)
188
+ * or the error
189
+ */
190
+ genericAdapterRequestNoBasePath(uriPath, restMethod, queryData, requestBody, addlHeaders, callback) {
191
+ const meth = 'genericHandler-genericAdapterRequestNoBasePath';
192
+ const origin = `${this.myid}-${meth}`;
193
+ log.trace(origin);
194
+
195
+ /* HERE IS WHERE YOU VALIDATE DATA */
196
+ if (uriPath === undefined || uriPath === null || uriPath === '') {
197
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['uriPath'], null, null, null);
198
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
199
+ return callback(null, errorObj);
200
+ }
201
+ if (restMethod === undefined || restMethod === null || restMethod === '') {
202
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['restMethod'], null, null, null);
203
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
204
+ return callback(null, errorObj);
205
+ }
206
+
207
+ /* HERE IS WHERE YOU SET THE DATA TO PASS INTO REQUEST */
208
+ // remove any leading / and split the uripath into path variables
209
+ let myPath = uriPath;
210
+ while (myPath.indexOf('/') === 0) {
211
+ myPath = myPath.substring(1);
212
+ }
213
+ const pathVars = myPath.split('/');
214
+ const queryParamsAvailable = queryData;
215
+ const queryParams = {};
216
+ const bodyVars = requestBody;
217
+
218
+ // loop in template. long callback arg name to avoid identifier conflicts
219
+ Object.keys(queryParamsAvailable).forEach((thisKeyInQueryParamsAvailable) => {
220
+ if (queryParamsAvailable[thisKeyInQueryParamsAvailable] !== undefined && queryParamsAvailable[thisKeyInQueryParamsAvailable] !== null
221
+ && queryParamsAvailable[thisKeyInQueryParamsAvailable] !== '') {
222
+ queryParams[thisKeyInQueryParamsAvailable] = queryParamsAvailable[thisKeyInQueryParamsAvailable];
223
+ }
224
+ });
225
+
226
+ // set up the request object - payload, uriPathVars, uriQuery, uriOptions, addlHeaders
227
+ const reqObj = {
228
+ payload: bodyVars,
229
+ uriPathVars: pathVars,
230
+ uriQuery: queryParams,
231
+ uriOptions: {}
232
+ };
233
+ // add headers if provided
234
+ if (addlHeaders) {
235
+ reqObj.addlHeaders = addlHeaders;
236
+ }
237
+
238
+ // determine the call and return flag
239
+ let action = 'getGenericsNoBase';
240
+ let returnF = true;
241
+ if (restMethod.toUpperCase() === 'POST') {
242
+ action = 'createGenericNoBase';
243
+ } else if (restMethod.toUpperCase() === 'PUT') {
244
+ action = 'updateGenericNoBase';
245
+ } else if (restMethod.toUpperCase() === 'PATCH') {
246
+ action = 'patchGenericNoBase';
247
+ } else if (restMethod.toUpperCase() === 'DELETE') {
248
+ action = 'deleteGenericNoBase';
249
+ returnF = false;
250
+ }
251
+
252
+ try {
253
+ // Make the call -
254
+ // identifyRequest(entity, action, requestObj, returnDataFlag, callback)
255
+ return this.requestHandlerInst.identifyRequest('.generic', action, reqObj, returnF, (irReturnData, irReturnError) => {
256
+ // if we received an error or their is no response on the results
257
+ // return an error
258
+ if (irReturnError) {
259
+ /* HERE IS WHERE YOU CAN ALTER THE ERROR MESSAGE */
260
+ return callback(null, irReturnError);
261
+ }
262
+ if (!Object.hasOwnProperty.call(irReturnData, 'response')) {
263
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Invalid Response', ['genericAdapterRequestNoBasePath'], null, null, null);
264
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
265
+ return callback(null, errorObj);
266
+ }
267
+
268
+ /* HERE IS WHERE YOU CAN ALTER THE RETURN DATA */
269
+ // return the response
270
+ return callback(irReturnData, null);
271
+ });
272
+ } catch (ex) {
273
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
274
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
275
+ return callback(null, errorObj);
276
+ }
277
+ }
278
+ }
279
+
280
+ module.exports = GenericHandler;
@@ -363,10 +363,16 @@ class AdapterPropertyUtil {
363
363
  if (entitySchema.responseDatatype && entitySchema.responseDatatype.toUpperCase() === 'PLAIN') {
364
364
  // read the mock date from the file system
365
365
  mockResponse.response = fs.readFileSync(mockFileName, 'utf-8');
366
+ if (!mockResponse.response) {
367
+ mockResponse.response = 'mock file empty!';
368
+ }
366
369
  } else if (entitySchema.responseDatatype && (entitySchema.responseDatatype.toUpperCase() === 'XML'
367
370
  || entitySchema.responseDatatype.toUpperCase() === 'XML2JSON')) {
368
371
  // read the mock date from the file system
369
372
  mockResponse.response = fs.readFileSync(mockFileName, 'utf-8');
373
+ if (!mockResponse.response) {
374
+ mockResponse.response = '<mock>file empty!</mock>';
375
+ }
370
376
  } else {
371
377
  // read the mock date from the file system
372
378
  try {
@@ -374,7 +380,7 @@ class AdapterPropertyUtil {
374
380
  mockResponse.response = JSON.parse(fs.readFileSync(mockFileName, 'utf-8'));
375
381
  } catch (excep) {
376
382
  log.warn(`${origin}: Could not parse file - ${mockFileName}`);
377
- mockResponse.response = '';
383
+ mockResponse.response = { mock: 'file empty or parse error!' };
378
384
  }
379
385
  }
380
386
  } else {
@@ -435,7 +441,6 @@ class AdapterPropertyUtil {
435
441
  origin,
436
442
  isError: true
437
443
  };
438
-
439
444
  try {
440
445
  // verify required data
441
446
  if (!entityName || typeof entityName !== 'string') {
@@ -854,7 +859,6 @@ class AdapterPropertyUtil {
854
859
  getEntitySchema(entityName, actionName, choosepath, dbUtils, callback) {
855
860
  const origin = `${this.myid}-propertyUtil-getEntitySchema`;
856
861
  log.trace(origin);
857
-
858
862
  // need to try to get the entity schema from the adapter database
859
863
  try {
860
864
  // call to get the adapter schema from the database
@@ -948,6 +952,133 @@ class AdapterPropertyUtil {
948
952
  return defaults;
949
953
  }
950
954
 
955
+ /**
956
+ * @summary Takes in an item that may have sensitive data and scrubs it before
957
+ * it would get logged.
958
+ *
959
+ * @function scrubSensitiveInfo
960
+ * @param {String/Object} inData - the data to scrub
961
+ * @param {Array} addItems - additional items to scrub
962
+ *
963
+ * @return {Object} the object with default values from the property schema
964
+ */
965
+ scrubSensitiveInfo(inData, addItems) {
966
+ const origin = `${this.myid}-propertyUtil-scrubSensitiveInfo`;
967
+ log.trace(origin);
968
+
969
+ // no reason to scan numbers, booleans or functions
970
+ if (!inData || typeof inData === 'number' || typeof inData === 'boolean' || typeof inData === 'function') {
971
+ return inData;
972
+ }
973
+
974
+ // This is the array of sensitive keys
975
+ let sensList = ['authorization', 'x-auth-token', 'x-csrf-token', 'x-amz-security-token', 'x-aws-ec2-metadata-token', 'cookie', 'set-cookie', 'token', 'tokenp2', 'user', 'username', 'passwd', 'password', 'api-key', 'client-id', 'client-secret', 'session', 'session-id'];
976
+
977
+ // add any additional items to scrub
978
+ if (addItems && Array.isArray(addItems) && addItems.length > 0) {
979
+ sensList = sensList.concat(addItems);
980
+ }
981
+
982
+ // going to use copy of data so we do not mess up input - if object will still need to assign it
983
+ let actualData = inData;
984
+
985
+ // if we are scrubbing an array
986
+ if (Array.isArray(actualData)) {
987
+ // need to go through each item in the array
988
+ for (let i = 0; i < actualData.length; i += 1) {
989
+ actualData[i] = this.scrubSensitiveInfo(actualData[i]);
990
+ }
991
+
992
+ // return the scrubbed array
993
+ return actualData;
994
+ }
995
+
996
+ // if we are scrubbbing a string (e.g. URL)
997
+ if (typeof actualData === 'string') {
998
+ // if it is a Stringified JSON
999
+ try {
1000
+ // need to see if it is stringified JSON
1001
+ actualData = JSON.parse(inData);
1002
+ // if this was able to be parsed, we should handle it as an object
1003
+ } catch (ex) {
1004
+ // if not JSON, can only scrub the query (e.g. after ?)
1005
+ actualData = inData;
1006
+ const dataParts = actualData.split('?');
1007
+
1008
+ // if there is no query data - we are done
1009
+ if (dataParts.length === 0) {
1010
+ return actualData;
1011
+ }
1012
+
1013
+ // start what we return
1014
+ let retData = `${dataParts[0]}?`;
1015
+ let count = 0;
1016
+
1017
+ // query format - key=value& or key=value{end of url}
1018
+ const queryData = dataParts[1].split('&');
1019
+
1020
+ // analyze the query fields
1021
+ for (let i = 0; i < queryData.length; i += 1) {
1022
+ // key of the query field
1023
+ const key = queryData[i].split('=');
1024
+ let found = false;
1025
+
1026
+ // add any & to separate query params
1027
+ if (count > 0) {
1028
+ retData += '&';
1029
+ }
1030
+
1031
+ // go through sensitive word list - maybe can use find in
1032
+ for (let j = 0; j < sensList.length; j += 1) {
1033
+ if (key.toUpperCase() === sensList[j].toUpperCase()) {
1034
+ // if sensitive, mask
1035
+ retData += `${key}=** masked **`;
1036
+ found = true;
1037
+ count += 1;
1038
+ break;
1039
+ }
1040
+ }
1041
+
1042
+ // if not sensitive - just append back on url
1043
+ if (!found) {
1044
+ retData += queryData[i];
1045
+ }
1046
+ }
1047
+
1048
+ // return the scrubbed string
1049
+ return retData;
1050
+ }
1051
+ }
1052
+
1053
+ // if we are scrubbing an object (or string that has been parsed)
1054
+ if (typeof actualData === 'object') {
1055
+ const retData = { ...actualData };
1056
+
1057
+ // go through each item in the object
1058
+ Object.keys(retData).forEach((key) => {
1059
+ // go deep through an object with recursive call
1060
+ if (typeof retData[key] === 'object') {
1061
+ retData[key] = this.scrubSensitiveInfo(retData[key]);
1062
+ } else {
1063
+ // go through sensitive word list - maybe can use find in
1064
+ for (let j = 0; j < sensList.length; j += 1) {
1065
+ if (key.toUpperCase() === sensList[j].toUpperCase()) {
1066
+ // if sensitive, mask
1067
+ retData[key] = '=** masked **';
1068
+ break;
1069
+ }
1070
+ }
1071
+ }
1072
+ });
1073
+
1074
+ // return the scrubbed object
1075
+ return retData;
1076
+ }
1077
+
1078
+ // if something we do not handle yet - just return the data
1079
+ return actualData;
1080
+ }
1081
+
951
1082
  /**
952
1083
  * @summary Takes in properties and the secondary properties and merges them so the returned
953
1084
  * object has secondary properties where no primary property values were provided.