@newrelic/security-agent 0.0.1

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 (87) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/CONTRIBUTING.md +23 -0
  3. package/LICENSE +43 -0
  4. package/README.md +98 -0
  5. package/THIRD_PARTY_NOTICES.md +2909 -0
  6. package/index.js +24 -0
  7. package/lib/instrumentation-security/core/constants.js +66 -0
  8. package/lib/instrumentation-security/core/event-constants.js +49 -0
  9. package/lib/instrumentation-security/core/request-manager.js +73 -0
  10. package/lib/instrumentation-security/core/route-manager.js +30 -0
  11. package/lib/instrumentation-security/core/sec-trace.js +134 -0
  12. package/lib/instrumentation-security/core/sec-utils.js +212 -0
  13. package/lib/instrumentation-security/core/security-metadata.js +34 -0
  14. package/lib/instrumentation-security/core/stack-trace.js +156 -0
  15. package/lib/instrumentation-security/hooks/director/nr-director.js +98 -0
  16. package/lib/instrumentation-security/hooks/express/nr-express.js +106 -0
  17. package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +155 -0
  18. package/lib/instrumentation-security/hooks/fs/nr-fs.js +391 -0
  19. package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +131 -0
  20. package/lib/instrumentation-security/hooks/http/nr-http.js +461 -0
  21. package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +110 -0
  22. package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +81 -0
  23. package/lib/instrumentation-security/hooks/mongodb/common.js +239 -0
  24. package/lib/instrumentation-security/hooks/mongodb/constants.js +91 -0
  25. package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +49 -0
  26. package/lib/instrumentation-security/hooks/mongodb/v2-mongo.js +91 -0
  27. package/lib/instrumentation-security/hooks/mongodb/v3-mongo.js +36 -0
  28. package/lib/instrumentation-security/hooks/mongodb/v4-mongo.js +17 -0
  29. package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +232 -0
  30. package/lib/instrumentation-security/hooks/native/nr-childProcess.js +131 -0
  31. package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +131 -0
  32. package/lib/instrumentation-security/hooks/require/nr-require.js +93 -0
  33. package/lib/instrumentation-security/hooks/restify/nr-restify.js +104 -0
  34. package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +91 -0
  35. package/lib/instrumentation-security/index.js +171 -0
  36. package/lib/nr-security-agent/index.js +170 -0
  37. package/lib/nr-security-agent/lib/core/Auth-headers.js +58 -0
  38. package/lib/nr-security-agent/lib/core/ContainerProperties.js +42 -0
  39. package/lib/nr-security-agent/lib/core/ECSProperties.js +34 -0
  40. package/lib/nr-security-agent/lib/core/FuzzFailEvent.js +27 -0
  41. package/lib/nr-security-agent/lib/core/LinkingMetadata.js +21 -0
  42. package/lib/nr-security-agent/lib/core/LogMessage.js +47 -0
  43. package/lib/nr-security-agent/lib/core/PodProperties.js +36 -0
  44. package/lib/nr-security-agent/lib/core/Policy.js +105 -0
  45. package/lib/nr-security-agent/lib/core/agent-status.js +56 -0
  46. package/lib/nr-security-agent/lib/core/agent.js +146 -0
  47. package/lib/nr-security-agent/lib/core/applicationinfo.js +634 -0
  48. package/lib/nr-security-agent/lib/core/commonUtils.js +411 -0
  49. package/lib/nr-security-agent/lib/core/connections/aws-lambda-client.js +55 -0
  50. package/lib/nr-security-agent/lib/core/connections/restclient.js +20 -0
  51. package/lib/nr-security-agent/lib/core/connections/websocket/index.js +25 -0
  52. package/lib/nr-security-agent/lib/core/connections/websocket/response/collecotor-policy-error-handler.js +18 -0
  53. package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +20 -0
  54. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +171 -0
  55. package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +47 -0
  56. package/lib/nr-security-agent/lib/core/connections/websocket/response/no-op-handler.js +12 -0
  57. package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +27 -0
  58. package/lib/nr-security-agent/lib/core/connections/websocket/response/unsupported-agent-handler.js +18 -0
  59. package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +14 -0
  60. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +443 -0
  61. package/lib/nr-security-agent/lib/core/event.js +186 -0
  62. package/lib/nr-security-agent/lib/core/health-check.js +159 -0
  63. package/lib/nr-security-agent/lib/core/json-file-handler.js +66 -0
  64. package/lib/nr-security-agent/lib/core/jsonValidator.js +149 -0
  65. package/lib/nr-security-agent/lib/core/lambda-delegated-task-service.js +90 -0
  66. package/lib/nr-security-agent/lib/core/logging.js +176 -0
  67. package/lib/nr-security-agent/lib/core/sec-agent-constants.js +173 -0
  68. package/lib/nr-security-agent/lib/core/sec-util.js +166 -0
  69. package/lib/nr-security-agent/lib/core/sha-size-util.js +338 -0
  70. package/lib/nr-security-agent/lib/core/sha_worker.js +24 -0
  71. package/lib/nr-security-agent/lib/core/startup-config.js +29 -0
  72. package/lib/nr-security-agent/lib/core/statusUtils.js +157 -0
  73. package/lib/nr-security-agent/lib/core/timeunit.js +44 -0
  74. package/lib/nr-security-agent/lib/core/websocket-client.js +335 -0
  75. package/lib/nr-security-agent/lib/core/worker-entities.js +82 -0
  76. package/lib/nr-security-agent/lib/core/worker.js +148 -0
  77. package/lib/nr-security-agent/lib/core/xss-utils.js +297 -0
  78. package/lib/nr-security-agent/lib/models/lambda-delegated-task/constants.js +12 -0
  79. package/lib/nr-security-agent/lib/models/lambda-delegated-task/index.js +15 -0
  80. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-sha-calculation.js +37 -0
  81. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-base.js +50 -0
  82. package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-type.js +17 -0
  83. package/lib/nr-security-agent/resources/cert.pem +31 -0
  84. package/lib/nr-security-agent/resources/config.js +19 -0
  85. package/lib/nr-security-agent/resources/default-policy.yaml +36 -0
  86. package/lib/nr-security-api/index.js +111 -0
  87. package/package.json +95 -0
@@ -0,0 +1,391 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ module.exports = initialize;
7
+ const requestManager = require("../../core/request-manager");
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const secUtils = require("../../core/sec-utils");
12
+ const API = require("../../../nr-security-api");
13
+ const securityMetaData = require('../../core/security-metadata');
14
+ const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
15
+ const logger = API.getLogger();
16
+ const { STRING, QUESTIONMARK, DOTDOTSLASH, UNDEFINED, SELF_FD_PATH, SLASH, SLASHDOTDOT, NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
17
+ const fsConsts = process.binding('constants').fs;
18
+ const agentModule = API.getSecAgent();
19
+
20
+ const requireHook = require('../require/nr-require');
21
+
22
+ const COPY_FILE = 'copyFile';
23
+ const RENAME = 'rename';
24
+ const FSReqCallback = 'FSReqCallback';
25
+ const FSReqWrap = 'FSReqWrap';
26
+ const OBJECT = 'object';
27
+
28
+ const functionsProbableToFA = [
29
+ "fstat",
30
+ "read",
31
+ "readBuffers",
32
+ "fdatasync",
33
+ "fsync",
34
+ "readdir",
35
+ "readlink",
36
+ "realpath",
37
+ ];
38
+
39
+ const functionProbableToFI = [
40
+ "rename",
41
+ "ftruncate",
42
+ "rmdir",
43
+ "symlink",
44
+ "link",
45
+ "unlink",
46
+ "fchmod",
47
+ "chmod",
48
+ "lchown",
49
+ "fchown",
50
+ "chown",
51
+ "utimes",
52
+ "futimes",
53
+ "lutimes",
54
+ "mkdtemp",
55
+ "copyFile",
56
+ "mkdir",
57
+ ];
58
+
59
+ const promiseFunctionsProbableToFA = [
60
+ "open",
61
+ "readdir",
62
+ "readlink",
63
+ "readFile",
64
+ "lstat",
65
+ "stat",
66
+ ];
67
+
68
+ const promiseFunctionProbableToFI = [
69
+ "copyFile",
70
+ "rename",
71
+ "truncate",
72
+ "rmdir",
73
+ "mkdir",
74
+ "symlink",
75
+ "link",
76
+ "unlink",
77
+ "chmod",
78
+ "lchmod",
79
+ "lchown",
80
+ "chown",
81
+ "utimes",
82
+ "lutimes",
83
+ "mkdtemp",
84
+ "writeFile",
85
+ "appendFile",
86
+ ];
87
+
88
+ /**
89
+ * init function to apply hook on fs module.
90
+ * @param {*} shim
91
+ * @param {*} mod
92
+ * @param {*} moduleName
93
+ */
94
+ function initialize(shim, mod, moduleName) {
95
+
96
+
97
+ logger.info("Instrumenting FS module");
98
+ requireHook.initialize(shim)
99
+ const binding = process.binding("fs");
100
+ openHook(shim, binding, moduleName);
101
+ probableToFAHooks(shim, mod, moduleName);
102
+ probableToFIHooks(shim, mod, moduleName);
103
+ const FSPromise = mod.promises;
104
+ if (FSPromise) {
105
+ probablePromiseToFAHooks(shim, FSPromise, moduleName);
106
+ probablePromiseToFIHooks(shim, FSPromise, moduleName);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Wrapper for fs.open() function.
112
+ * @param {*} shim
113
+ * @param {*} mod
114
+ * @param {*} moduleName
115
+ */
116
+ function openHook(shim, mod, moduleName) {
117
+ shim.wrap(mod, "open", function makeOpenWrapper(shim, fn) {
118
+ return function openWrapper() {
119
+ const parameters = Array.prototype.slice.apply(arguments);
120
+ const interceptedArgs = [arguments[0]];
121
+ shim.interceptedArgs = interceptedArgs;
122
+ const request = requestManager.getRequest(shim);
123
+ if ((parameters[0] != undefined) && (typeof parameters[0] !== STRING) && (typeof parameters[0] !== UNDEFINED) && (parameters[0] !== UNDEFINED)) {
124
+ try {
125
+ parameters[0] = fs.readlinkSync(SELF_FD_PATH.concat(parameters[0]));
126
+ } catch (err) {
127
+ parameters[0] = arguments[0];
128
+ }
129
+ }
130
+ if (request && typeof parameters[0] === STRING) {
131
+ const policy = API.getPolicy();
132
+ const dynamicScanningFlag = policy.data ? policy.data.vulnerabilityScan.iastScan.enabled : false;
133
+ const url = request.url;
134
+ const decodedURL = decodeURI(url);
135
+ const trimedURL = url.split(QUESTIONMARK)[0];
136
+ if (!trimedURL.includes(path.basename(parameters[0])) || decodedURL.includes(DOTDOTSLASH) || dynamicScanningFlag) {
137
+ try {
138
+ if (parameters[0].startsWith(DOTDOTSLADH)) {
139
+ parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0];
140
+ } else if (parameters[0].startsWith(SLASHDOTDOT)) {
141
+ parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0];
142
+ } else {
143
+ parameters[0] = path.resolve(parameters[0]);
144
+ }
145
+ } catch (error) {
146
+
147
+ }
148
+ const traceObject = secUtils.getTraceObject(shim);
149
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCaseType(parameters[1], parameters[0]), EVENT_CATEGORY.FILE)
150
+ this.secEvent = API.generateSecEvent(secMetadata);
151
+ API.sendEvent(this.secEvent);
152
+ const callbackFlag = isCallback(arguments);
153
+ if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
154
+ callbackHook(shim, arguments[3], 'oncomplete', this.secEvent);
155
+ }
156
+ }
157
+
158
+ }
159
+ const result = fn.apply(this, arguments);
160
+ if(result>0 && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
161
+ //here generate exit event.
162
+ API.generateExitEvent(this.secEvent);
163
+ delete this.secEvent;
164
+ }
165
+ return result;
166
+ };
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Wrapper to hook all the function provided in list functionsProbableToFA
172
+ * @param {*} shim
173
+ * @param {*} mod
174
+ * @param {*} moduleName
175
+ */
176
+ function probableToFAHooks(shim, mod, moduleName) {
177
+ functionsProbableToFA.forEach(function (fun) {
178
+ shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
179
+ return function FAWrapper() {
180
+ const interceptedArgs = [arguments[0]];
181
+ shim.interceptedArgs = interceptedArgs;
182
+ const request = requestManager.getRequest(shim);
183
+ if (request && typeof arguments[0] === STRING) {
184
+ const traceObject = secUtils.getTraceObject(shim);
185
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
186
+ this.secEvent = API.generateSecEvent(secMetadata);
187
+ API.sendEvent(this.secEvent);
188
+ const callbackFlag = isCallback(arguments);
189
+ if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
190
+ callbackHook(shim, arguments, arguments.length-1, this.secEvent);
191
+ }
192
+ }
193
+ return fn.apply(this, arguments);
194
+ };
195
+ });
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Wrapper to hook all the function provided in list functionProbableToFI
201
+ * @param {*} shim
202
+ * @param {*} mod
203
+ * @param {*} moduleName
204
+ */
205
+ function probableToFIHooks(shim, mod, moduleName) {
206
+ functionProbableToFI.forEach(function (fun) {
207
+ shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
208
+ return function FIWrapper() {
209
+ const interceptedArgs = [arguments[0]];
210
+ shim.interceptedArgs = interceptedArgs;
211
+ const request = requestManager.getRequest(shim);
212
+ if (request && typeof arguments[0] === STRING) {
213
+ const traceObject = secUtils.getTraceObject(shim);
214
+ if (fun === COPY_FILE || fun === RENAME) {
215
+ const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
216
+ this.secEvent = API.generateSecEvent(secMetadata);
217
+ API.sendEvent(this.secEvent);
218
+ }
219
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCase(arguments[0]), EVENT_CATEGORY.FILE)
220
+ this.secEvent = API.generateSecEvent(secMetadata);
221
+ API.sendEvent(this.secEvent);
222
+ const callbackFlag = isCallback(arguments);
223
+ if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
224
+ callbackHook(shim, arguments, arguments.length-1, this.secEvent);
225
+ }
226
+ }
227
+ return fn.apply(this, arguments);
228
+ };
229
+ });
230
+ });
231
+ }
232
+
233
+ /**
234
+ * Wrapper to hook all the function provided in list promiseFunctionsProbableToFA
235
+ * @param {*} shim
236
+ * @param {*} mod
237
+ * @param {*} moduleName
238
+ */
239
+ function probablePromiseToFAHooks(shim, mod, moduleName) {
240
+ promiseFunctionsProbableToFA.forEach(function (fun) {
241
+ shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
242
+ return function FAWrapper() {
243
+ const interceptedArgs = [arguments[0]];
244
+ shim.interceptedArgs = interceptedArgs;
245
+ const request = requestManager.getRequest(shim);
246
+ if (request && typeof arguments[0] === STRING) {
247
+ const traceObject = secUtils.getTraceObject(shim);
248
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
249
+ this.secEvent = API.generateSecEvent(secMetadata);
250
+ API.sendEvent(this.secEvent);
251
+ }
252
+ return fn.apply(this, arguments);
253
+ };
254
+ });
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Wrapper to hook all the function provided in list promiseFunctionProbableToFI
260
+ * @param {*} shim
261
+ * @param {*} mod
262
+ * @param {*} moduleName
263
+ */
264
+ function probablePromiseToFIHooks(shim, mod, moduleName) {
265
+ promiseFunctionProbableToFI.forEach(function (fun) {
266
+ shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
267
+ return function FIWrapper() {
268
+ const interceptedArgs = [arguments[0]];
269
+ shim.interceptedArgs = interceptedArgs;
270
+ const request = requestManager.getRequest(shim);
271
+ if (request && typeof arguments[0] === STRING) {
272
+ const traceObject = secUtils.getTraceObject(shim);
273
+ if (fun === COPY_FILE || fun === RENAME) {
274
+ const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
275
+ this.secEvent = API.generateSecEvent(secMetadata);
276
+ API.sendEvent(this.secEvent);
277
+ }
278
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCase(arguments[0]), EVENT_CATEGORY.FILE)
279
+ this.secEvent = API.generateSecEvent(secMetadata);
280
+ API.sendEvent(this.secEvent);
281
+ }
282
+ return fn.apply(this, arguments);
283
+ };
284
+ });
285
+ });
286
+ }
287
+
288
+ /**
289
+ * Utility to get case type based on flag and file path in case of file open operation
290
+ * @param {*} flag
291
+ * @param {*} filePath
292
+ * @returns
293
+ */
294
+ function getCaseType(flag, filePath) {
295
+ if (flag) {
296
+ switch (flag) {
297
+ case fsConsts.O_RDONLY:
298
+ case fsConsts.O_RDONLY | fsConsts.O_SYNC:
299
+ return EVENT_TYPE.FILE_OPERATION;
300
+ case fsConsts.O_RDWR:
301
+ case fsConsts.O_RDWR | fsConsts.O_SYNC:
302
+ case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_WRONLY:
303
+ case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_EXCL:
304
+ case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_RDWR:
305
+ case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_EXCL:
306
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY:
307
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_EXCL:
308
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_SYNC:
309
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR:
310
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_EXCL:
311
+ case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_SYNC:
312
+ filePath = path.resolve(filePath);
313
+ const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
314
+ const isInPath = isPathInside(filePath, appPath);
315
+ if (typeof filePath === STRING && isInPath) {
316
+ return EVENT_TYPE.FILE_INTEGRITY;
317
+ }
318
+ }
319
+ }
320
+ return EVENT_TYPE.FILE_OPERATION;
321
+ }
322
+
323
+ /**
324
+ * Utility to get case type based on provided file path
325
+ * @param {*} filePath
326
+ * @returns
327
+ */
328
+ function getCase(filePath) {
329
+ filePath = path.resolve(filePath);
330
+ const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
331
+ const isInPath = isPathInside(filePath, appPath);
332
+ if (typeof filePath === STRING && isInPath) {
333
+ return EVENT_TYPE.FILE_INTEGRITY;
334
+ } else {
335
+ return EVENT_TYPE.FILE_OPERATION;
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Utility to check if child path is inside parent path
341
+ * @param {*} childPath
342
+ * @param {*} parentPath
343
+ * @returns
344
+ */
345
+ function isPathInside(childPath, parentPath) {
346
+ childPath = path.resolve(childPath);
347
+ parentPath = path.resolve(parentPath);
348
+
349
+ if (childPath === parentPath) {
350
+ return false;
351
+ }
352
+
353
+ childPath += path.sep;
354
+ parentPath += path.sep;
355
+
356
+ return childPath.startsWith(parentPath);
357
+ }
358
+ function isCallback(function_args) {
359
+ try {
360
+ const obj = function_args[function_args.length - 1];
361
+ if (obj && typeof function_args[function_args.length - 1] === OBJECT && (obj.constructor.name === FSReqCallback || obj.constructor.name === FSReqWrap)) {
362
+ return true;
363
+ }
364
+ } catch (error) {
365
+ return false;
366
+ }
367
+ return false;
368
+ }
369
+
370
+ /**
371
+ * Callback hook to generate exit event
372
+ * @param {*} shim
373
+ * @param {*} mod
374
+ * @param {*} fun
375
+ * @param {*} secEvent
376
+ */
377
+ function callbackHook(shim, mod, fun, secEvent) {
378
+ shim.secEvent = secEvent;
379
+ shim.wrap(mod, fun, function callbackWrapper(shim, fn) {
380
+ if (!shim.isFunction(fn)) {
381
+ return fn;
382
+ }
383
+ return function wrapper() {
384
+ if ((arguments[0] === null || arguments[0] === undefined) && shim.secEvent) {
385
+ API.generateExitEvent(shim.secEvent);
386
+ delete shim.secEvent;
387
+ }
388
+ return fn.apply(this, arguments);
389
+ }
390
+ })
391
+ }
@@ -0,0 +1,131 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+
7
+ 'use strict'
8
+
9
+ /**
10
+ * Express middleware generates traces where middleware are considered siblings
11
+ * (ended on 'next' invocation) and not nested. Middlware are nested below the
12
+ * routers they are mounted to.
13
+ */
14
+
15
+ const requestManager = require("../../core/request-manager");
16
+ const secUtils = require('../../core/sec-utils');
17
+ const { ATTHERATE, DOUBLE_DOLLAR } = require('../../core/constants');
18
+ const lodash = require('lodash');
19
+ const routeManager = require('../../core/route-manager');
20
+ const API = require("../../../nr-security-api");
21
+ const logger = API.getLogger();
22
+
23
+ module.exports = function initialize(shim, hapi, moduleName) {
24
+ if (!hapi || !shim) {
25
+ return
26
+ }
27
+
28
+ logger.info("Instrumenting Hapi")
29
+ // 'Server' and 'server' both point to the same export,
30
+ // but we can't make any assumption about which will be used.
31
+ // Since we wrap the prototype, the second wrap should exit early.
32
+ shim.wrapReturn(hapi, 'server', serverFactoryWrapper)
33
+ shim.wrapReturn(hapi, 'Server', serverFactoryWrapper)
34
+ }
35
+
36
+ function serverFactoryWrapper(shim, fn, fnName, server) {
37
+ serverPostConstructor.call(server, shim)
38
+ }
39
+
40
+ function serverPostConstructor(shim) {
41
+ const proto = Object.getPrototypeOf(this)
42
+ wrapProtoRoute(shim, proto)
43
+ }
44
+
45
+ /**
46
+ * Wrapper for route
47
+ * @param {*} shim
48
+ * @param {*} proto
49
+ */
50
+ function wrapProtoRoute(shim, proto) {
51
+ shim.wrap(proto, 'route', function wrapRoute(shim, original) {
52
+ return function wrappedRoute() {
53
+ const args = shim.argsToArray.apply(shim, arguments)
54
+ if (!shim.isObject(args[0])) {
55
+ return original.apply(this, args)
56
+ }
57
+ const obj = args[0];
58
+ for (let i = 0; i < obj.length; i++) {
59
+ try {
60
+ const method = lodash.upperCase(obj[i].method);
61
+ const path = obj[i].path;
62
+ const stakTrace = secUtils.traceElementForRoute();
63
+ const splittedStack = stakTrace[0].split(DOUBLE_DOLLAR);
64
+ const key = method + ATTHERATE + path;
65
+ routeManager.setRoute(key, splittedStack[0]);
66
+ } catch (error) {
67
+
68
+ }
69
+ if (obj[i].handler) {
70
+ handlerWrapper(shim, obj[i]);
71
+ }
72
+ }
73
+
74
+ return original.apply(this, args)
75
+ }
76
+ })
77
+ }
78
+
79
+ /**
80
+ * Wrapper to hook each handler corresponding to route
81
+ * @param {*} shim
82
+ * @param {*} mod
83
+ */
84
+ function handlerWrapper(shim, mod) {
85
+ shim.wrap(mod, 'handler', function wrapHandler(shim, original) {
86
+ if (!shim.isFunction(original)) {
87
+ return original
88
+ }
89
+ return function wrappedHandler() {
90
+ extractParams(shim, arguments[0]);
91
+ return original.apply(this, arguments);
92
+ }
93
+ })
94
+ }
95
+
96
+
97
+ /**
98
+ * Utility function to parse request obejct intercepted from express.router.process_params() method hook
99
+ * @param {*} shim
100
+ * @param {*} req
101
+ */
102
+ function extractParams(shim, req) {
103
+ const segment = shim.getActiveSegment();
104
+ if (segment && segment.transaction) {
105
+ let request = requestManager.getRequestFromId(segment.transaction.id);
106
+ if (req.params && request) {
107
+ Object.keys(req.params).forEach(function (key) {
108
+ if (req.params[key]) {
109
+ if (!request.parameterMap[key]) {
110
+ request.parameterMap[key] = new Array(req.params[key].toString());
111
+ requestManager.setRequest(segment.transaction.id, request);
112
+ }
113
+ }
114
+ });
115
+ }
116
+ if (req.query && request) {
117
+ Object.keys(req.query).forEach(function (key) {
118
+ if (req.query[key]) {
119
+ if (!request.parameterMap[key]) {
120
+ request.parameterMap[key] = new Array(req.query[key].toString());
121
+ requestManager.setRequest(segment.transaction.id, request);
122
+ }
123
+ }
124
+ });
125
+ }
126
+ if (req.route && req.route.path && request) {
127
+ request.uri = req.route.path;
128
+ requestManager.setRequest(segment.transaction.id, request);
129
+ }
130
+ }
131
+ }