@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,461 @@
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 { NR_CSEC_FUZZ_REQUEST_ID, QUESTION_MARK, EMPTY_STRING, UTF8, CONTENT_TYPE, TEXT_HTML, APPLICATION_JSON, APPLICATION_XML, APPLICATION_XHTML, TEXT_PLAIN, APPLICATION_X_FORM_URLENCODED, MULTIPART_FORM_DATA } = require('../../core/constants');
9
+ const ARRAY_TYPE = 'Array';
10
+ const STRING_TYPE = 'string';
11
+ const BUFFER_TYPE = 'Buffer';
12
+ const UINT8ARRAY_TYPE = 'Uint8Array';
13
+ const OBJECT_TYPE = 'object'
14
+ const unescapeJs = require('unescape-js');
15
+ const ConType = require('content-type');
16
+ const lodash = require('lodash');
17
+ const URL = require('url')
18
+ const secUtils = require('../../core/sec-utils');
19
+ const API = require("../../../nr-security-api");
20
+ const logger = API.getLogger();
21
+ const NRAgent = API.getNRAgent();
22
+ const securityMetaData = require('../../core/security-metadata');
23
+ const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
24
+ const hc = require('../../../nr-security-agent/lib/core/health-check');
25
+
26
+ const NRCSECTRACINGDATA = 'NR-CSEC-TRACING-DATA';
27
+ const SELFTEST = 'self-test';
28
+
29
+ const requestIp = require('request-ip');
30
+ const isInvalid = require('is-invalid-path');
31
+ const path = require('path');
32
+ const fs = require('fs');
33
+ const semver = require('semver');
34
+
35
+
36
+ const CSEC_SEP = ':IAST:';
37
+ const find = '{{NR_CSEC_VALIDATOR_HOME_TMP}}';
38
+ const CSEC_HOME_TMP_CONST = new RegExp(find, 'g');
39
+ const CSEC_HOME = NRAgent && NRAgent.config && NRAgent.config.newrelic_home ? NRAgent.config.newrelic_home : require('path').join(process.cwd());
40
+ const CSEC_HOME_TMP = `${CSEC_HOME}/nr-security-home/tmp/language-agent/${process.env.applicationUUID}`
41
+
42
+ /**
43
+ * Entry point of http module hook
44
+ * @param {*} shim
45
+ * @param {*} mod
46
+ * @param {*} moduleName
47
+ */
48
+ function initialize(shim, mod, moduleName) {
49
+ logger.info('Instrumenting HTTP module');
50
+ emitHook(shim, mod, moduleName);
51
+ createServerHook(shim, mod, moduleName);
52
+ outboundHook(shim, mod, 'request', moduleName);
53
+ if (semver.gte(process.version, '8.0.0')) {
54
+ outboundHook(shim, mod, 'get', moduleName);
55
+ }
56
+ setupRequestMapCleanup()
57
+ }
58
+
59
+ /**
60
+ * Adds a listener for when a transaction finishes
61
+ * so the request manager map entry can be cleaned up
62
+ */
63
+ function setupRequestMapCleanup() {
64
+ if (NRAgent) {
65
+ NRAgent.on('transactionFinished', function onTransactionFinished(transaction) {
66
+ const requestData = requestManager.getRequestFromId(transaction.id);
67
+ if (requestData && requestData.tempFiles && !lodash.isEmpty(requestData.tempFiles)) {
68
+ const tempFiles = requestData.tempFiles;
69
+ for (let i = 0; i < tempFiles.length; i++) {
70
+ const file = tempFiles[i];
71
+ deleteTempFileForIAST(file);
72
+ }
73
+ }
74
+ // Here removing request data corresponding to transactionId
75
+ requestManager.gcRequestMap(transaction.id)
76
+ })
77
+ }
78
+ }
79
+
80
+
81
+ /**
82
+ * Hook to intercept outbound http requests.
83
+ * @param {*} shim
84
+ * @param {*} mod
85
+ * @param {*} moduleName
86
+ */
87
+ function outboundHook(shim, mod, method, moduleName) {
88
+ shim.wrap(mod, method, function makeRequestWrapper(shim, fn) {
89
+ return function requestWrapper() {
90
+ const request = requestManager.getRequest(shim);
91
+
92
+ const data = {};
93
+ data.headers = {};
94
+ data.method = arguments[0].method;
95
+ data.path = arguments[0].path;
96
+ data.protocol = arguments[0].protocol ? arguments[0].protocol : 'http:';
97
+ data.headers.host = arguments[0].hostname ? arguments[0].hostname : arguments[0].host;
98
+ data.port = arguments[0].port;
99
+
100
+ if (method === 'get') {
101
+ data.method = 'GET';
102
+ data.path = '/';
103
+ data.headers.host = arguments[0];
104
+ try {
105
+ data.headers.host = data.headers.host.replace(/(^\w+:|^)\/\//, '');
106
+ } catch (error) {
107
+
108
+ }
109
+ }
110
+
111
+ let completeURL = data.protocol + '//' + data.headers.host + (data.port ? (':' + data.port) : '') + data.path;
112
+ completeURL = completeURL.split(QUESTION_MARK)[0];
113
+
114
+ if (request && !lodash.isEmpty(data.headers.host) && !lodash.isEmpty(data.path)) {
115
+ const appUUID = API.getSecAgent().applicationInfo.applicationUUID;
116
+
117
+ const id = secUtils.getExecutionId().toString();
118
+ let generateFlag = false;
119
+ if (arguments[0] && arguments[0].headers && arguments[0].headers.SECHOOK === id) {
120
+ generateFlag = true;
121
+ }
122
+ if (!generateFlag) {
123
+ const traceObject = secUtils.getTraceObject(shim);
124
+ const secMetadata = securityMetaData.getSecurityMetaData(request, data, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
125
+ this.secEvent = API.generateSecEvent(secMetadata);
126
+ API.sendEvent(this.secEvent);
127
+ if (request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
128
+ callbackHook(shim, arguments, arguments.length - 1, this.secEvent);
129
+ }
130
+ }
131
+
132
+ let traceHeader = EMPTY_STRING;
133
+
134
+ if (request.headers['nr-csec-tracing-data']) {
135
+ traceHeader = request.headers['nr-csec-tracing-data'];
136
+ }
137
+
138
+ if (arguments[0] instanceof Object) {
139
+ if (arguments[0] && arguments[0].headers && request.headers) {
140
+ arguments[0].headers.SECHOOK = id;
141
+ arguments[0].headers[NRCSECTRACINGDATA] = traceHeader + appUUID + '/' + this.secEvent.apiId + '/' + id + ';';
142
+ } else {
143
+ arguments[0].headers = {};
144
+ arguments[0].headers[NRCSECTRACINGDATA] = traceHeader + appUUID + '/' + this.secEvent.apiId + '/' + id + ';';
145
+ arguments[0].headers.SECHOOK = id;
146
+ }
147
+ if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID] && arguments[0].headers) {
148
+ arguments[0].headers[NR_CSEC_FUZZ_REQUEST_ID] = request.headers[NR_CSEC_FUZZ_REQUEST_ID];
149
+ }
150
+ }
151
+ }
152
+ return fn.apply(this, arguments);
153
+ };
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Function to parse fuzzheaders and create temp files in case of dynamic scanning
159
+ * Not functional
160
+ * @param {*} fuzzheaders
161
+ */
162
+ function parseFuzzheaders(requestData, transactionId) {
163
+ let fuzzheaders = requestData.headers;
164
+ const policy = API.getPolicy();
165
+ const dynamicScanningFlag = policy.data ? policy.data.vulnerabilityScan.iastScan.enabled : false;
166
+ if (fuzzheaders[NR_CSEC_FUZZ_REQUEST_ID] && dynamicScanningFlag) {
167
+ try {
168
+ const additionalData = fuzzheaders[NR_CSEC_FUZZ_REQUEST_ID].split(CSEC_SEP);
169
+ logger.debug('AdditionalData:', additionalData);
170
+ if (additionalData.length >= 7) {
171
+ for (let i = 6; i < additionalData.length; i++) {
172
+ let file = additionalData[i].trim();
173
+ file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
174
+ file = path.resolve(file);
175
+ const parentDir = path.dirname(file);
176
+ if (!isInvalid(parentDir) && isPathInside(parentDir, CSEC_HOME_TMP)) {
177
+ try {
178
+ if (!fs.existsSync(parentDir)) {
179
+ secUtils.createPathIfNotExist(parentDir);
180
+ } else {
181
+ logger.debug(parentDir + ' Already Exists');
182
+ }
183
+ fs.closeSync(fs.openSync(file, 'w'));
184
+
185
+ requestData.tempFiles = [];
186
+ requestData.tempFiles.push(file);
187
+ requestManager.setRequest(transactionId, requestData);
188
+
189
+ } catch (error) {
190
+ logger.debug(error);
191
+ }
192
+ }
193
+ }
194
+ }
195
+ } catch (error) {
196
+ logger.debug(error);
197
+ }
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Utility function to add request data in a Map
203
+ * @param {*} shim
204
+ * @param {*} request
205
+ * @param {*} resp
206
+ */
207
+ function addRequestData(shim, request) {
208
+ try {
209
+ const data = Object.assign({});
210
+ const segment = shim.getActiveSegment();
211
+ if (segment && segment.transaction) {
212
+ data.body = null;
213
+ data.headers = request.headers;
214
+ data.url = request.url;
215
+ data.method = request.method;
216
+ data.httpVersion = request.httpVersion;
217
+ data.serverPort = segment.transaction.port;
218
+ data.contextPath = '/';
219
+ const queryObject = URL.parse(request.url, true).query;
220
+ data.parameterMap = {};
221
+ if (queryObject) {
222
+ Object.keys(queryObject).forEach(function (key) {
223
+ if (queryObject[key]) {
224
+ if (!data.parameterMap[key]) {
225
+ data.parameterMap[key] = new Array(queryObject[key].toString());
226
+ }
227
+ }
228
+ });
229
+ }
230
+ data.clientIP = requestIp.getClientIp(request);
231
+ const transactionId = segment.transaction.id;
232
+ const storedRequest = requestManager.getRequestFromId(transactionId);
233
+ if (storedRequest && storedRequest.uri) {
234
+ data.uri = storedRequest.uri;
235
+ data.parameterMap = storedRequest.parameterMap;
236
+ }
237
+ requestManager.setRequest(transactionId, data);
238
+ parseFuzzheaders(data, transactionId);
239
+ }
240
+ } catch (error) {
241
+ logger.debug("Error while preparing incoming request:", error);
242
+ }
243
+
244
+ }
245
+
246
+ /**
247
+ * Wrappert to hook http.server.emit()
248
+ * @param {*} shim
249
+ * @param {*} mod
250
+ * @param {*} moduleName
251
+ */
252
+ function emitHook(shim, mod, moduleName) {
253
+ shim.wrap(mod && mod.Server && mod.Server.prototype, 'emit', function makeEmitWrapper(shim, fn) {
254
+ return function emitWrapper() {
255
+ const req = arguments[1];
256
+ const resp = arguments[2];
257
+
258
+ if (arguments[0] == 'request') {
259
+ if (NRAgent && NRAgent.config.security.detection.rxss.enabled) {
260
+ responseHook(resp, req, shim);
261
+ }
262
+
263
+ shim.wrap(req, 'on', function makeOnWrapper(shim, fn) {
264
+ return function OnWrapper() {
265
+ if (!requestManager.getRequest(shim)) {
266
+ addRequestData(shim, req);
267
+ }
268
+ if (arguments[0] === 'end') {
269
+ if (req && req.headers[SELFTEST] != 1) {
270
+ hc.getInstance().registerHttpRequestCount();
271
+ }
272
+ }
273
+ if (arguments[0] === 'data') {
274
+ onDataHook(shim, arguments)
275
+ }
276
+ return fn.apply(this, arguments);
277
+ }
278
+ })
279
+ }
280
+
281
+ return fn.apply(this, arguments);
282
+ }
283
+ })
284
+ }
285
+
286
+ /**
287
+ * Wrapper for http.createServer method
288
+ * @param {*} shim
289
+ * @param {*} mod
290
+ */
291
+ function createServerHook(shim, mod) {
292
+ shim.wrap(mod, 'createServer', function createServerWrapper(shim, fn) {
293
+ return function wrappedCreateServer() {
294
+ shim.wrap(arguments, '0', function appWrapper(shim, fn) {
295
+ return function wrappedApp() {
296
+ let req = arguments[0];
297
+ if (!requestManager.getRequest(shim)) {
298
+ addRequestData(shim, req);
299
+ }
300
+ return fn.apply(this, arguments);
301
+ }
302
+ })
303
+ return fn.apply(this, arguments);
304
+ }
305
+ })
306
+ }
307
+
308
+ /**
309
+ * Wrapper to hook http request body
310
+ * @param {*} shim
311
+ * @param {*} mod
312
+ */
313
+ function onDataHook(shim, mod) {
314
+ shim.wrap(mod, '1', function makeOnDataWrapper(shim, fn) {
315
+ return function onDataWrapper() {
316
+ const chunk = arguments[0];
317
+ const chunkType = typeof chunk;
318
+ segment = shim.getActiveSegment()
319
+ if (chunkType === STRING_TYPE || chunkType === ARRAY_TYPE || chunkType === BUFFER_TYPE || chunkType === UINT8ARRAY_TYPE || chunkType === OBJECT_TYPE) {
320
+ let data = chunk.toString();
321
+ const transactionId = segment.transaction.id;
322
+ const requestData = requestManager.getRequestFromId(transactionId);
323
+ if (requestData) {
324
+ data = requestData.body ? requestData.body.concat(chunk.toString()) : chunk.toString();
325
+ requestManager.updateRequestBody(shim, data);
326
+ }
327
+
328
+ }
329
+ return fn.apply(this, arguments);
330
+ }
331
+ })
332
+ }
333
+
334
+ /**
335
+ * Wrapper to hook response.write() and reponse.end() method
336
+ * @param {*} resp
337
+ * @param {*} req
338
+ * @param {*} shim
339
+ */
340
+ function responseHook(resp, req, shim) {
341
+ // wrapper for response.write()
342
+ resp.res = {};
343
+ resp.res.body = EMPTY_STRING;
344
+
345
+ shim.wrap(resp, 'write', function makeWriteWrapper(shim, fn) {
346
+ return function wrappedWrite() {
347
+ const response = this;
348
+ responseBodyCompute(response, arguments);
349
+ return fn.apply(this, arguments);
350
+ }
351
+ })
352
+ // wrapper for response.end()
353
+ shim.wrap(resp, 'end', function makeEndWrapper(shim, fn) {
354
+ return function wrappedEnd() {
355
+ const response = this;
356
+ responseBodyCompute(response, arguments);
357
+ const request = requestManager.getRequest(shim);
358
+ const construct = API.checkForReflectedXSS(request, response.res.body, response.getHeaders());
359
+ const policy = API.getPolicy();
360
+ const dynamicScanningFlag = policy.data ? (policy.data.vulnerabilityScan?.enabled && policy.data.vulnerabilityScan.iastScan.enabled) : false;
361
+ if (request && (construct || dynamicScanningFlag)) {
362
+ const args = [];
363
+ args.push(construct);
364
+ args.push(response.res.body);
365
+ const traceObject = secUtils.getTraceObject(shim);
366
+ const secMetadata = securityMetaData.getSecurityMetaData(request, args, traceObject, secUtils.getExecutionId(), EVENT_TYPE.REFLECTED_XSS, EVENT_CATEGORY.REFLECTED_XSS)
367
+ const secEvent = API.generateSecEvent(secMetadata);
368
+
369
+ API.sendEvent(secEvent);
370
+ }
371
+ return fn.apply(this, arguments);
372
+ }
373
+ })
374
+ }
375
+
376
+ function responseBodyCompute(response, args) {
377
+ let encoding = UTF8;
378
+ const type = response.getHeader(CONTENT_TYPE);
379
+ let contentType = TEXT_HTML;
380
+ if (type) {
381
+ const obj = ConType.parse(type);
382
+ contentType = obj.type;
383
+ encoding = obj.parameters.charset;
384
+ }
385
+ if (args[0]) {
386
+ switch (contentType) {
387
+ case APPLICATION_JSON:
388
+ response.res.body += unescapeJs(args[0].toString(encoding));
389
+ break;
390
+ case APPLICATION_XML:
391
+ case APPLICATION_XHTML:
392
+ case TEXT_PLAIN:
393
+ case TEXT_HTML:
394
+ case APPLICATION_X_FORM_URLENCODED:
395
+ case MULTIPART_FORM_DATA: {
396
+ const data = args[0].toString(encoding);
397
+ response.res.body += data;
398
+ break;
399
+ }
400
+ }
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Callback hook to generate exit event
406
+ * @param {*} shim
407
+ * @param {*} mod
408
+ * @param {*} fun
409
+ * @param {*} secEvent
410
+ */
411
+ function callbackHook(shim, mod, fun, secEvent) {
412
+ shim.secEvent = secEvent;
413
+ shim.wrap(mod, fun, function callbackWrapper(shim, fn) {
414
+ if (!shim.isFunction(fn)) {
415
+ return fn;
416
+ }
417
+ return function wrapper() {
418
+ if (arguments && shim.secEvent) {
419
+ API.generateExitEvent(shim.secEvent);
420
+ delete shim.secEvent;
421
+ }
422
+ return fn.apply(this, arguments);
423
+ }
424
+ })
425
+ }
426
+
427
+ /**
428
+ * Utility to check if child path is inside parent path
429
+ * @param {*} childPath
430
+ * @param {*} parentPath
431
+ * @returns
432
+ */
433
+ function isPathInside(childPath, parentPath) {
434
+ childPath = path.resolve(childPath);
435
+ parentPath = path.resolve(parentPath);
436
+
437
+ if (childPath === parentPath) {
438
+ return false;
439
+ }
440
+
441
+ childPath += path.sep;
442
+ parentPath += path.sep;
443
+
444
+ return childPath.startsWith(parentPath);
445
+ }
446
+
447
+
448
+ /**
449
+ * Delete Temporary file for IAST. Do not call this in RASP
450
+ * @param {*} file
451
+ */
452
+ function deleteTempFileForIAST(file) {
453
+ try {
454
+ if (fs.existsSync(file)) {
455
+ logger.debug("Deleting temporary file:", file);
456
+ fs.unlinkSync(file);
457
+ }
458
+ } catch (error) {
459
+ logger.debug("Unable to delete temporary file", error);
460
+ }
461
+ }
@@ -0,0 +1,110 @@
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 secUtils = require('../../../core/sec-utils');
9
+ const { ATTHERATE, DOUBLE_DOLLAR } = require('../../../core/constants');
10
+ const lodash = require('lodash');
11
+ const routeManager = require('../../../core/route-manager');
12
+ const url = require('url');
13
+ const API = require("../../../../nr-security-api");
14
+ const logger = API.getLogger();
15
+
16
+ /**
17
+ * Entry point of koa-router module hook
18
+ * @param {*} shim
19
+ * @param {*} mod
20
+ * @param {*} moduleName
21
+ */
22
+ function initialize(shim, mod, moduleName) {
23
+ logger.info('Instrumenting @koa/router');
24
+ const VerbMethods = ['all', 'delete', 'get', 'head', 'opts', 'post', 'put', 'patch'];
25
+
26
+ // RouteMap hooks
27
+ VerbMethods.forEach(function (fun) {
28
+ shim.wrap(mod && mod.prototype, fun, function wrapRoute(shim, fn) {
29
+ if (!shim.isFunction(fn)) {
30
+ return fn
31
+ }
32
+ return function wrappedRoute() {
33
+ try {
34
+ const stakTrace = secUtils.traceElementForRoute();
35
+ const splittedStack = stakTrace[0].split(DOUBLE_DOLLAR);
36
+ const key = lodash.upperCase(splittedStack[1]) + ATTHERATE + arguments[0];
37
+ routeManager.setRoute(key, splittedStack[0]);
38
+ } catch (error) {
39
+ }
40
+ const route = fn.apply(this, arguments);
41
+ return route;
42
+ }
43
+ })
44
+ });
45
+
46
+ try {
47
+ const layerModule = shim.require("./lib/layer")
48
+ paramHook(shim, layerModule, moduleName);
49
+ } catch (error) {
50
+ }
51
+
52
+
53
+ }
54
+ /**
55
+ * Wrapper for params method, to intercept path params and query params
56
+ * @param {*} shim
57
+ * @param {*} mod
58
+ * @param {*} moduleName
59
+ */
60
+ function paramHook(shim, mod, moduleName) {
61
+ shim.wrap(mod && mod.prototype, 'params', function wrapMatch(shim, fn) {
62
+ return function wrappedMatch() {
63
+ const segment = shim.getActiveSegment();
64
+ if (segment && segment.transaction) {
65
+ let request = requestManager.getRequestFromId(segment.transaction.id);
66
+ try {
67
+ const uri = this.path;
68
+ if (request) {
69
+ request.uri = uri;
70
+ requestManager.setRequest(segment.transaction.id, request);
71
+ }
72
+
73
+ } catch (error) {
74
+
75
+ }
76
+
77
+ const result = fn.apply(this, arguments);
78
+ try {
79
+ var url_parts = url.parse(request.url, true);
80
+ // logic to get query param
81
+ if (url_parts.query) {
82
+ Object.keys(url_parts.query).forEach(function (key) {
83
+ if (url_parts.query[key]) {
84
+ request.parameterMap[key] = new Array(url_parts.query[key].toString());
85
+ }
86
+ });
87
+ }
88
+ // logic to get path param
89
+ if (!lodash.isEmpty(result)) {
90
+ Object.keys(result).forEach(function (key) {
91
+ if (result[key]) {
92
+ if (!request.parameterMap[key]) {
93
+ request.parameterMap[key] = new Array(result[key].toString());
94
+ }
95
+ }
96
+ });
97
+ }
98
+ requestManager.setRequest(segment.transaction.id, request);
99
+ } catch (error) {
100
+
101
+ }
102
+ return result;
103
+ }
104
+ else{
105
+ return fn.apply(this,arguments);
106
+ }
107
+
108
+ }
109
+ })
110
+ }
@@ -0,0 +1,81 @@
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 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 } = require('../../core/constants');
13
+ const logger = API.getLogger();
14
+
15
+ /**
16
+ * Entry point of ldapjs and ldapts module hook
17
+ * @param {*} shim
18
+ * @param {*} mod
19
+ * @param {*} moduleName
20
+ */
21
+ function initialize(shim, mod, moduleName){
22
+ logger.info('Instrumenting '+ moduleName )
23
+
24
+ if(moduleName==='ldapjs' || moduleName==='ldapts'){
25
+ ldapHooks(shim, mod && mod.Client && mod.Client.prototype, 'search', moduleName);
26
+ }
27
+
28
+ }
29
+ /**
30
+ * wrapper to hook ldapjs and ldapts module
31
+ * @param {*} shim
32
+ * @param {*} mod
33
+ * @param {*} methodName
34
+ * @param {*} moduleName
35
+ */
36
+ function ldapHooks(shim, mod, methodName, moduleName){
37
+ shim.wrap(mod, methodName, function makeWrapper(shim,fn){
38
+ return function wrapper(){
39
+ const param = {};
40
+ param.name = arguments[0];
41
+ param.filter = arguments[1].filter;
42
+
43
+ const interceptedArgs = [param];
44
+ shim.interceptedArgs = interceptedArgs;
45
+ const request = requestManager.getRequest(shim);
46
+ if(request){
47
+ const traceObject = secUtils.getTraceObject(shim);
48
+ const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.LDAP, EVENT_CATEGORY.LDAP)
49
+ const secEvent = API.generateSecEvent(secMetadata);
50
+ API.sendEvent(secEvent);
51
+ if (request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
52
+ callbackHook(shim, arguments, arguments.length - 1, secEvent);
53
+ }
54
+ }
55
+ return fn.apply(this, arguments);
56
+ }
57
+ })
58
+ }
59
+
60
+ /**
61
+ * Callback hook to generate exit event
62
+ * @param {*} shim
63
+ * @param {*} mod
64
+ * @param {*} fun
65
+ * @param {*} secEvent
66
+ */
67
+ function callbackHook(shim, mod, fun, secEvent) {
68
+ shim.secEvent = secEvent;
69
+ shim.wrap(mod, fun, function callbackWrapper(shim, fn) {
70
+ if (!shim.isFunction(fn)) {
71
+ return fn;
72
+ }
73
+ return function wrapper() {
74
+ if ((arguments[0] === null || arguments[0] === undefined) && shim.secEvent) {
75
+ API.generateExitEvent(shim.secEvent);
76
+ delete shim.secEvent;
77
+ }
78
+ return fn.apply(this, arguments);
79
+ }
80
+ })
81
+ }