@newrelic/security-agent 1.2.0 → 1.4.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.
Files changed (27) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/THIRD_PARTY_NOTICES.md +212 -211
  4. package/lib/instrumentation-security/core/constants.js +1 -0
  5. package/lib/instrumentation-security/core/grpc-utils.js +53 -0
  6. package/lib/instrumentation-security/core/security-metadata.js +12 -2
  7. package/lib/instrumentation-security/hooks/fs/nr-fs.js +4 -4
  8. package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +342 -0
  9. package/lib/instrumentation-security/hooks/http/nr-http.js +46 -6
  10. package/lib/instrumentation-security/index.js +32 -3
  11. package/lib/nr-security-agent/index.js +7 -1
  12. package/lib/nr-security-agent/lib/core/ExceptionReporting.js +182 -0
  13. package/lib/nr-security-agent/lib/core/LogMessage.js +4 -1
  14. package/lib/nr-security-agent/lib/core/commonUtils.js +2 -2
  15. package/lib/nr-security-agent/lib/core/connections/grpcClient.js +112 -0
  16. package/lib/nr-security-agent/lib/core/connections/restclient.js +6 -0
  17. package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +3 -0
  18. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +32 -12
  19. package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +1 -1
  20. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +9 -4
  21. package/lib/nr-security-agent/lib/core/event.js +1 -0
  22. package/lib/nr-security-agent/lib/core/health-check.js +0 -1
  23. package/lib/nr-security-agent/lib/core/sec-util.js +1 -0
  24. package/lib/nr-security-agent/lib/core/statusUtils.js +1 -1
  25. package/lib/nr-security-agent/lib/core/websocket-client.js +2 -1
  26. package/lib/nr-security-agent/lib/core/xss-utils.js +11 -5
  27. package/package.json +4 -3
@@ -0,0 +1,342 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: New Relic Pre-Release
4
+ */
5
+
6
+
7
+ const requestManager = require("../../core/request-manager");
8
+ const secUtils = require("../../core/sec-utils");
9
+ const API = require("../../../nr-security-api");
10
+ const securityMetaData = require('../../core/security-metadata');
11
+ const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
12
+ const { NR_CSEC_FUZZ_REQUEST_ID, DOUBLE_DOLLAR, ATTHERATE, ASTERISK } = require('../../core/constants');
13
+ const logger = API.getLogger();
14
+ const semver = require('semver');
15
+ const grpcutils = require('../../core/grpc-utils');
16
+ const routeManager = require('../../core/route-manager');
17
+ let serverAddress;
18
+ const ExceptionReporting = require('../../../nr-security-agent/lib/core/ExceptionReporting');
19
+
20
+
21
+
22
+ module.exports.initialize = function initialize(shim, grpc, moduleName) {
23
+ logger.info('Instrumenting grpc', moduleName);
24
+ const grpcVersion = shim.require('./package.json').version;
25
+ logger.debug("grpc version:", grpcVersion);
26
+
27
+ if (semver.lt(grpcVersion, '1.4.0')) {
28
+ logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
29
+ return
30
+ }
31
+
32
+ if (semver.gte(grpcVersion, '1.8.0')) {
33
+ const resolvingCall = shim.require('./build/src/resolving-call')
34
+ shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
35
+ } else {
36
+ const callStream = shim.require('./build/src/call-stream')
37
+ shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
38
+ }
39
+
40
+ const server = shim.require('./build/src/server')
41
+ shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
42
+ shim.wrap(server.Server.prototype, 'register', wrapRegister)
43
+
44
+ //get all service
45
+ shim.wrap(server.Server.prototype, 'addService', wrapAddService);
46
+
47
+ //load package definition
48
+ const makeClient = shim.require('./build/src/make-client');
49
+ shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
50
+
51
+ }
52
+
53
+ module.exports.wrapStartResolve = function wrappedStartResolve(shim, resolvingCall) {
54
+ shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
55
+ }
56
+
57
+ module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
58
+ shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
59
+ }
60
+
61
+ module.exports.wrapServer = function wrappedServer(shim, server) {
62
+ const grpcVersion = shim.require('./package.json').version
63
+ logger.info("Instrumenting @grpc/grpc-js", grpcVersion)
64
+ if (semver.lt(grpcVersion, '1.4.0')) {
65
+ logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
66
+ return
67
+ }
68
+
69
+ shim.wrap(server.Server.prototype, 'register', wrapRegister)
70
+ shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
71
+ //get all service
72
+ shim.wrap(server.Server.prototype, 'addService', wrapAddService);
73
+ }
74
+
75
+ module.exports.wrapMakeClient = function wrapMakeClient(shim, makeClient) {
76
+ shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
77
+ }
78
+
79
+
80
+ /**
81
+ * Instruments grpc-js client by intercepting the function that
82
+ * initiates client requests. This handles all four types of client
83
+ * invocations: unary, client-streaming, server-streaming, and
84
+ * bidirectional streaming.
85
+ *
86
+ * @param {object} shim the generic shim to instrument with
87
+ * @param {Function} original the original function
88
+ * @returns {Function} the instrumented function
89
+ */
90
+ function wrapStart(shim, original) {
91
+ if (!shim.isFunction(original)) {
92
+ return original
93
+ }
94
+ return function wrappedStart() {
95
+ const channel = this.channel;
96
+ const authorityName = (channel.target && channel.target.path) || channel.getDefaultAuthority;
97
+ // in 1.8.0 this changed from methodName to method
98
+ const method = this.methodName || this.method;
99
+ const protocol = 'grpc:';
100
+ const url = `${protocol}//${authorityName}${method}`;
101
+ logger.debug("grpc url is:", url)
102
+ const request = requestManager.getRequest(shim);
103
+ let urlObj = {
104
+ "headers":
105
+ {
106
+ "host": authorityName
107
+ },
108
+ "method": method,
109
+ "path": method,
110
+ "protocol": protocol,
111
+ }
112
+ if (request) {
113
+ const traceObject = secUtils.getTraceObject(shim);
114
+ const secMetadata = securityMetaData.getSecurityMetaData(request, urlObj, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
115
+ const secEvent = API.generateSecEvent(secMetadata);
116
+ API.sendEvent(secEvent);
117
+ }
118
+
119
+ const res = original.apply(this, arguments);
120
+ return res;
121
+ }
122
+ }
123
+
124
+
125
+ /**
126
+ * Wrapper for addService method
127
+ * @param {*} shim
128
+ * @param {*} original
129
+ * @returns
130
+ */
131
+ function wrapAddService(shim, original) {
132
+ if (!shim.isFunction(original)) {
133
+ return original
134
+ }
135
+ return function wrappedbindAsync() {
136
+ try {
137
+ const stakTrace = secUtils.traceElementForRoute();
138
+ const splittedStack = stakTrace[0].split(DOUBLE_DOLLAR);
139
+ let serviceValues = Object.values(arguments[0]);
140
+ for (const [key, value] of Object.entries(serviceValues)) {
141
+ const routeKey = ASTERISK + ATTHERATE + value.path;
142
+ let finalHandler = value.originalName + ATTHERATE + splittedStack[0];
143
+ routeManager.setRoute(routeKey, finalHandler);
144
+ }
145
+ } catch (error) {
146
+ }
147
+ const result = original.apply(this, arguments);
148
+ return result;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * wrapper for loadPackageDefinition method
154
+ * @param {*} shim
155
+ * @param {*} original
156
+ * @returns
157
+ */
158
+ function wrapLoadPackageDefinition(shim, original) {
159
+ if (!shim.isFunction(original)) {
160
+ return original
161
+ }
162
+ return function wrappedbindAsync() {
163
+ let serviceObject = arguments[0];
164
+ const result = original.apply(this, arguments);
165
+ iterateNestedObjectForServiceLookup(result, serviceObject);
166
+ return result;
167
+ }
168
+ }
169
+
170
+
171
+
172
+
173
+ function bindAsyncWrapper(shim, original) {
174
+ if (!shim.isFunction(original)) {
175
+ return original
176
+ }
177
+ return function wrappedbindAsync() {
178
+ serverAddress = arguments[0];
179
+ shim.grpcServerAddress = serverAddress;
180
+ try {
181
+ const splittedData = serverAddress.split(":");
182
+ serverPort = splittedData[1];
183
+ shim.serverPort = parseInt(serverPort);
184
+ } catch (error) {
185
+
186
+ }
187
+ const result = original.apply(this, arguments);
188
+ return result;
189
+ }
190
+ }
191
+
192
+
193
+
194
+ /**
195
+ * Instruments the grpc-js server by intercepting the moment when
196
+ * server methods are registered from the method implementations
197
+ * provided to grpc-js. This handles all four types of server
198
+ * invocations: unary, client-streaming, server-streaming, and
199
+ * bidirectional streaming.
200
+ *
201
+ * @param {object} shim the web shim to instrument with
202
+ * @param {Function} original the original function
203
+ * @returns {Function} the instrumented function
204
+ */
205
+ function wrapRegister(shim, original) {
206
+ if (!shim.isFunction(original)) {
207
+ return original
208
+ }
209
+ return function wrappedRegister() {
210
+ wrapHandler(shim, arguments, '1');
211
+ const result = original.apply(this, arguments);
212
+ return result;
213
+ }
214
+ }
215
+
216
+
217
+ /**
218
+ * Wrapper to hook handler function
219
+ * @param {*} shim
220
+ * @param {*} mod
221
+ * @param {*} method
222
+ */
223
+ function wrapHandler(shim, mod, method) {
224
+ shim.wrap(mod, method, function wrapHandler(shim, fn) {
225
+ if (!shim.isFunction(fn)) {
226
+ return fn
227
+ }
228
+ return function wrappedHandler() {
229
+ const type = this.type;
230
+ const path = this.path;
231
+ wrapCallback(shim, arguments, '1');
232
+ addRequestData(shim, type, path, arguments[0]);
233
+ const result = fn.apply(this, arguments);
234
+ return result;
235
+
236
+ }
237
+ })
238
+ }
239
+
240
+ /**
241
+ * Wrapper to hook callback response
242
+ * @param {*} shim
243
+ * @param {*} mod
244
+ * @param {*} method
245
+ */
246
+ function wrapCallback(shim, mod, method) {
247
+ shim.wrap(mod, method, function wrapCallback(shim, fn) {
248
+ if (!shim.isFunction(fn)) {
249
+ return fn
250
+ }
251
+ return function wrappedCallback() {
252
+ const response = arguments[1];
253
+ try {
254
+ const request = requestManager.getRequest(shim);
255
+ if (arguments[0]) {
256
+ if (arguments[0].details && request) {
257
+ ExceptionReporting.generateExceptionReportingEvent(arguments[0].details, request);
258
+ }
259
+ //TODO GRPC response errors reporting
260
+ // if (arguments[0].code && arguments[0].code > 0 && request) {
261
+ // let responseCode = arguments[0].code
262
+ // ExceptionReporting.generate5xxReportingEvent(null, request, responseCode);
263
+ // }
264
+ }
265
+ } catch (error) {
266
+
267
+ }
268
+ const result = fn.apply(this, arguments);
269
+ return result;
270
+
271
+ }
272
+ })
273
+ }
274
+
275
+ /**
276
+ * Utility function to add grpc request data in request map
277
+ * @param {*} shim
278
+ * @param {*} type
279
+ * @param {*} path
280
+ * @param {*} args
281
+ */
282
+ function addRequestData(shim, type, path, args) {
283
+ const data = Object.assign({});
284
+ try {
285
+ const segment = shim.getActiveSegment();
286
+ if (segment && segment.transaction) {
287
+ data.method = path;
288
+ data.url = path;
289
+ data.uri = path;
290
+ data.headers = args.metadata.getMap();
291
+ if (data.headers && data.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
292
+ let bufferObj = Buffer.from(data.headers[NR_CSEC_FUZZ_REQUEST_ID], "base64");
293
+ let decodedString = bufferObj.toString("utf8");
294
+ data.headers[NR_CSEC_FUZZ_REQUEST_ID] = decodedString;
295
+ }
296
+ data.type = type;
297
+ data.protocol = 'grpc';
298
+ data.isGrpc = true;
299
+ data.body = JSON.stringify(args.request);
300
+ data.serverPort = shim.serverPort;
301
+ const transactionId = segment.transaction.id;
302
+ requestManager.setRequest(transactionId, data);
303
+ }
304
+ } catch (error) {
305
+ logger.error("Error occured while extracting grpc request", error);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Utilty to set grpc util Maps
311
+ * @param {*} serviceObject
312
+ */
313
+ function serviceUtil(serviceObject) {
314
+ let serviceValues = Object.values(serviceObject)[0];
315
+ for (const [key, value] of Object.entries(serviceValues)) {
316
+ let servObj = {
317
+ 'serviceName': serviceObject.serviceName,
318
+ 'originalName': value.originalName,
319
+ }
320
+ grpcutils.setMethod(value.path, servObj);
321
+ }
322
+ }
323
+
324
+ function iterateNestedObjectForServiceLookup(result, serviceObject) {
325
+ try {
326
+ for (const key in result) {
327
+ if (typeof result[key] === 'object' && result[key] !== 'null') {
328
+ iterateNestedObjectForServiceLookup(result[key], serviceObject); // Recurse into nested object
329
+ } else {
330
+ let value = result[key];
331
+ if (value && value.serviceName) {
332
+ if (!value.type && value.serviceName) {
333
+ grpcutils.setService(value.serviceName, serviceObject);
334
+ serviceUtil(value);
335
+ }
336
+ }
337
+ }
338
+ }
339
+ } catch (error) {
340
+ logger.error("Error while getting service definition", error);
341
+ }
342
+ }
@@ -31,13 +31,16 @@ const isInvalid = require('is-invalid-path');
31
31
  const path = require('path');
32
32
  const fs = require('fs');
33
33
  const semver = require('semver');
34
-
34
+ const ExceptionReporting = require('../../../nr-security-agent/lib/core/ExceptionReporting');
35
35
 
36
36
  const CSEC_SEP = ':IAST:';
37
37
  const find = '{{NR_CSEC_VALIDATOR_HOME_TMP}}';
38
38
  const CSEC_HOME_TMP_CONST = new RegExp(find, 'g');
39
39
  let CSEC_HOME = NRAgent && NRAgent.config.newrelic_home ? NRAgent.config.newrelic_home : NRAgent && NRAgent.config.logging.filepath ? path.dirname(NRAgent.config.logging.filepath) : require('path').join(process.cwd());
40
40
  const CSEC_HOME_TMP = `${CSEC_HOME}/nr-security-home/tmp/language-agent/${process.env.applicationUUID}`
41
+ let lastTransactionId = EMPTY_STRING;
42
+ let uncaughtExceptionReportFlag = false;
43
+
41
44
 
42
45
  /**
43
46
  * Entry point of http module hook
@@ -178,7 +181,7 @@ function parseFuzzheaders(requestData, transactionId) {
178
181
  if (verifiedHash) {
179
182
  logger.debug("Encrypted Data:", encryptedData);
180
183
  logger.debug("Decrypted Data:", decryptedData)
181
- logger.debug("fliesTocreate:",filesToCreate);
184
+ logger.debug("fliesTocreate:", filesToCreate);
182
185
  for (let i = 0; i < filesToCreate.length; i++) {
183
186
  let file = filesToCreate[i].trim();
184
187
  file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
@@ -192,11 +195,11 @@ function parseFuzzheaders(requestData, transactionId) {
192
195
  logger.debug(parentDir + ' Already Exists');
193
196
  }
194
197
  fs.closeSync(fs.openSync(file, 'w'));
195
-
198
+
196
199
  requestData.tempFiles = [];
197
200
  requestData.tempFiles.push(file);
198
201
  requestManager.setRequest(transactionId, requestData);
199
-
202
+
200
203
  } catch (error) {
201
204
  logger.debug(error);
202
205
  }
@@ -243,6 +246,7 @@ function addRequestData(shim, request) {
243
246
  data.clientIP = requestIp.getClientIp(request);
244
247
  const transactionId = segment.transaction.id;
245
248
  const storedRequest = requestManager.getRequestFromId(transactionId);
249
+ lastTransactionId = transactionId;
246
250
  if (storedRequest && storedRequest.uri) {
247
251
  data.uri = storedRequest.uri;
248
252
  data.parameterMap = storedRequest.parameterMap;
@@ -396,8 +400,27 @@ function responseHook(resp, req, shim) {
396
400
  }
397
401
  return function wrappedEnd() {
398
402
  const response = this;
399
- responseBodyCompute(response, arguments);
400
403
  const request = requestManager.getRequest(shim);
404
+ const txInfo = lodash.get(response, Object.getOwnPropertySymbols(response).find(symbol => symbol.toString() === 'Symbol(transactionInfo)'));
405
+ try {
406
+ if (txInfo && txInfo.error && request) {
407
+ ExceptionReporting.generateExceptionReportingEvent(txInfo.error, request);
408
+ }
409
+ const responseCode = response.statusCode;
410
+ if (responseCode && parseInt(responseCode / 100) == 5 && request) {
411
+ if (txInfo && txInfo.error) {
412
+ ExceptionReporting.generate5xxReportingEvent(txInfo.error, request, responseCode);
413
+ }
414
+ else {
415
+ ExceptionReporting.generate5xxReportingEvent(null, request, responseCode);
416
+ }
417
+ }
418
+ } catch (error) {
419
+ logger.debug("Error while reporting error", error);
420
+ }
421
+
422
+ responseBodyCompute(response, arguments);
423
+
401
424
  const construct = API.checkForReflectedXSS(request, response.res.body, response.getHeaders());
402
425
  const policy = API.getPolicy();
403
426
  const dynamicScanningFlag = policy.data ? (policy.data.vulnerabilityScan?.enabled && policy.data.vulnerabilityScan.iastScan.enabled) : false;
@@ -565,4 +588,21 @@ function deleteTempFileForIAST(file) {
565
588
  } catch (error) {
566
589
  logger.debug("Unable to delete temporary file", error);
567
590
  }
568
- }
591
+ }
592
+ /**
593
+ * Collect and report the uncaught Exception
594
+ */
595
+ process.on('uncaughtException', (err, origin) => {
596
+ console.error(err);
597
+ if(uncaughtExceptionReportFlag){
598
+ process.exit(1);
599
+ }
600
+ const request = requestManager.getRequestFromId(lastTransactionId);
601
+ const exception = new ExceptionReporting.exceptionReporting(err, request);
602
+ if (exception) {
603
+ API.sendEvent(exception);
604
+ }
605
+ logger.error("An uncaughtException is detected:", err);
606
+ uncaughtExceptionReportFlag = true;
607
+ });
608
+
@@ -199,10 +199,39 @@ newrelic.instrumentWebframework({
199
199
  }
200
200
  })
201
201
 
202
+ newrelic.instrument({
203
+ moduleName: '@grpc/grpc-js/build/src/server',
204
+ isEsm: true,
205
+ onRequire: require('./hooks/grpc-js/nr-grpc').wrapServer,
206
+ onError: function intrumentErrorHandler(err) {
207
+ logger.error(err.message, err.stack)
208
+ }
209
+ })
202
210
 
211
+ newrelic.instrument({
212
+ moduleName: '@grpc/grpc-js/build/src/make-client',
213
+ isEsm: true,
214
+ onRequire: require('./hooks/grpc-js/nr-grpc').wrapMakeClient,
215
+ onError: function intrumentErrorHandler(err) {
216
+ logger.error(err.message, err.stack)
217
+ }
218
+ })
203
219
 
220
+ newrelic.instrument({
221
+ moduleName: '@grpc/grpc-js/build/src/resolving-call',
222
+ isEsm: true,
223
+ onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartResolve,
224
+ onError: function intrumentErrorHandler(err) {
225
+ logger.error(err.message, err.stack)
226
+ }
227
+ })
204
228
 
205
-
206
-
207
-
229
+ newrelic.instrument({
230
+ moduleName: '@grpc/grpc-js/build/src/call-stream',
231
+ isEsm: true,
232
+ onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartCall,
233
+ onError: function intrumentErrorHandler(err) {
234
+ logger.error(err.message, err.stack)
235
+ }
236
+ })
208
237
 
@@ -16,6 +16,13 @@ const njsAgentConstants = require('./lib/core/sec-agent-constants');
16
16
 
17
17
  const NRLogger = API.newrelic.shim.logger.child({ component: 'security-agent' });
18
18
  NRLogger.info("Starting New Relic Node.js Security Agent ");
19
+ try {
20
+ if (NRAgent.config.security.enabled){
21
+ require('../instrumentation-security');
22
+ }
23
+ } catch (error) {
24
+ logger.error("Error while applying security instrumentation")
25
+ }
19
26
 
20
27
  const {
21
28
  IS_LAMBDA_ENV,
@@ -169,7 +176,6 @@ function getProcCMDLineSanitized(applicationInfoModule) {
169
176
  return;
170
177
  }
171
178
  if (initialize()) {
172
- require('../instrumentation-security');
173
179
  initLogger.info("[STEP-6] => Application instrumentation applied successfully");
174
180
  }
175
181
  })();
@@ -0,0 +1,182 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: New Relic Software License v1.0
4
+ */
5
+
6
+ const LinkingMetaData = require('./LinkingMetadata');
7
+ const API = require('../../../nr-security-api');
8
+ const NRAgent = API.getNRAgent();
9
+ const { BasicInfo } = require('./event');
10
+ const applicationInfo = require('./applicationinfo').getInstance();
11
+ const exceptionsMap = new Map();
12
+ const logs = require('./logging');
13
+ const logger = logs.getLogger();
14
+ const exceptionReportingInterval = 30000;
15
+ const http = require('http');
16
+ const shaUtil = require('./sha-size-util');
17
+
18
+ const NR_LIB = '/newrelic/lib';
19
+
20
+ const GRPCStatusMap = {
21
+ 0: 'OK',
22
+ 1: 'CANCELLED',
23
+ 2: 'UNKNOWN',
24
+ 3: 'INVALID_ARGUMENT',
25
+ 4: 'DEADLINE_EXCEEDED',
26
+ 5: 'NOT_FOUND',
27
+ 6: 'ALREADY_EXISTS',
28
+ 7: 'PERMISSION_DENIED',
29
+ 8: 'RESOURCE_EXHAUSTED',
30
+ 9: 'FAILED_PRECONDITION',
31
+ 10: 'ABORTED',
32
+ 11: 'OUT_OF_RANGE',
33
+ 12: 'UNIMPLEMENTED',
34
+ 13: 'INTERNAL',
35
+ 14: 'UNAVAILABLE',
36
+ 15: 'DATA_LOSS',
37
+ 16: 'UNAUTHENTICATED',
38
+ }
39
+
40
+ /**
41
+ * constructor function to create createException object
42
+ * @param {*} message
43
+ * @param {*} cause
44
+ * @param {*} stacktrace
45
+ */
46
+ function createException(exception) {
47
+ let exceptionObject = {};
48
+ try {
49
+ exceptionObject.message = exception.message ? exception.message : '';
50
+ exceptionObject.cause = null;
51
+ exceptionObject.type = exception.name ? exception.name : 'Error';
52
+ if (exception.stack) {
53
+ let stacktraceInList = exception.stack.split('\n');
54
+ stacktraceInList = stacktraceInList.slice(1);
55
+ let fileteredStackTrace = [];
56
+ for (const str of stacktraceInList) {
57
+ if (!str.includes(NR_LIB) && str.length > 0) {
58
+ fileteredStackTrace.push(str.trim());
59
+ }
60
+ }
61
+ exceptionObject.stackTrace = fileteredStackTrace;
62
+ }
63
+
64
+ } catch (error) {
65
+ }
66
+
67
+ return exceptionObject;
68
+ }
69
+
70
+ /**
71
+ * constructor function to create exceptionReporting object;
72
+ * @param {*} timestamp
73
+ * @param {*} level
74
+ * @param {*} message
75
+ * @param {*} caller
76
+ * @param {*} exception
77
+ * @param {*} linkingMetadata
78
+ */
79
+ function exceptionReporting(exception, request, responseCode) {
80
+ try {
81
+ BasicInfo.call(this);
82
+ this.jsonVersion = applicationInfo.jsonVersion;
83
+ this.pid = applicationInfo.pid ? applicationInfo.pid : null;
84
+ this.jsonName = 'application-runtime-error';
85
+ this.applicationUUID = applicationInfo.applicationUUID;
86
+ this.policyVersion = applicationInfo.policyVersion;
87
+ this.collectorVersion = applicationInfo.collectorVersion ? applicationInfo.collectorVersion : null;
88
+ this.buildNumber = applicationInfo.buildNumber ? applicationInfo.buildNumber : null;
89
+ this.timestamp = Date.now();
90
+ this.httpRequest = request;
91
+ this.httpRequest.route = request.uri ? request.uri : request.url;
92
+ this.exception = null;
93
+ this.counter = 1;
94
+ this.responseCode = responseCode ? responseCode : null
95
+
96
+ if (exception) {
97
+ this.exception = createException(exception);
98
+ this.category = exception.name;
99
+ this.traceId = shaUtil.getSHA256ForData(this.exception.stackTrace.join('|') + this.category + this.httpRequest.route + this.httpRequest.method);
100
+ }
101
+ else {
102
+ this.traceId = shaUtil.getSHA256ForData(this.category + this.httpRequest.route + this.httpRequest.method);
103
+ }
104
+ if (responseCode) {
105
+ if (responseCode >= 500) {
106
+ this.category = http.STATUS_CODES[responseCode];
107
+ this.traceId = shaUtil.getSHA256ForData(this.category + this.httpRequest.route + this.httpRequest.method);
108
+ }
109
+ else {
110
+ this.category = GRPCStatusMap[responseCode];
111
+ this.traceId = shaUtil.getSHA256ForData(this.category + this.httpRequest.route + this.httpRequest.method);
112
+ }
113
+ }
114
+
115
+ if (NRAgent) {
116
+ this.linkingMetadata = LinkingMetaData.getLinkingMetadata();
117
+ }
118
+ } catch (error) {
119
+ logger.debug("Error while generating exceptionReporing", error);
120
+ }
121
+
122
+ }
123
+
124
+ function generateExceptionReportingEvent(error, request) {
125
+ try {
126
+ const exception = new exceptionReporting(error, request);
127
+ let key = exception.exception.type + exception.exception.stackTrace[0];
128
+ if (!exceptionsMap.has(key)) {
129
+ exceptionsMap.set(key, exception);
130
+ }
131
+ else {
132
+ let previousCounter = exceptionsMap.get(key).counter;
133
+ exception.counter = previousCounter ? previousCounter + 1: exception.counter + 1;
134
+ exceptionsMap.set(key, exception);
135
+ }
136
+ } catch (err) {
137
+ logger.debug("Error while generating application-runtime-error:", err);
138
+ }
139
+ }
140
+
141
+ function generate5xxReportingEvent(error, request, responseCode) {
142
+ try {
143
+ const exception = new exceptionReporting(error, request, responseCode);
144
+ let key = (request.uri ? request.uri : request.url) + responseCode;
145
+ if (!exceptionsMap.has(key)) {
146
+ exceptionsMap.set(key, exception);
147
+ }
148
+ else {
149
+ let previousCounter = exceptionsMap.get(key).counter;
150
+ exception.counter = previousCounter ? previousCounter + 1: exception.counter + 1;
151
+ exceptionsMap.set(key, exception);
152
+ }
153
+ } catch (err) {
154
+ logger.debug("Error while generating 5xx application-runtime-error:", err);
155
+ }
156
+ }
157
+
158
+
159
+
160
+ function startExceptionSendingSchedular() {
161
+ try {
162
+ setInterval(() => {
163
+ const values = Array.from(exceptionsMap.values());
164
+ for (let i = 0; i < values.length; i++) {
165
+ API.sendEvent(values[i]);
166
+ }
167
+ exceptionsMap.clear();
168
+ }, exceptionReportingInterval);
169
+ } catch (error) {
170
+ logger.info("Error in exception reporting schedular");
171
+ }
172
+ logger.info("Exception reporting schedular started");
173
+ }
174
+
175
+
176
+ module.exports = {
177
+ exceptionReporting,
178
+ createException,
179
+ generateExceptionReportingEvent,
180
+ generate5xxReportingEvent,
181
+ startExceptionSendingSchedular
182
+ };