@newrelic/security-agent 1.1.1 → 1.3.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 (26) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -1
  3. package/THIRD_PARTY_NOTICES.md +211 -1
  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/sec-utils.js +99 -57
  7. package/lib/instrumentation-security/core/security-metadata.js +12 -2
  8. package/lib/instrumentation-security/hooks/express/nr-express.js +43 -3
  9. package/lib/instrumentation-security/hooks/fs/nr-fs.js +4 -4
  10. package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +301 -0
  11. package/lib/instrumentation-security/hooks/http/nr-http.js +34 -23
  12. package/lib/instrumentation-security/index.js +32 -3
  13. package/lib/nr-security-agent/index.js +7 -1
  14. package/lib/nr-security-agent/lib/core/Policy.js +0 -1
  15. package/lib/nr-security-agent/lib/core/commonUtils.js +19 -4
  16. package/lib/nr-security-agent/lib/core/connections/grpcClient.js +112 -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/sec-agent-constants.js +2 -1
  22. package/lib/nr-security-agent/lib/core/sec-util.js +3 -1
  23. package/lib/nr-security-agent/lib/core/statusUtils.js +7 -6
  24. package/lib/nr-security-agent/lib/core/websocket-client.js +1 -1
  25. package/lib/nr-security-agent/lib/core/xss-utils.js +11 -5
  26. package/package.json +3 -2
@@ -138,9 +138,9 @@ function openHook(shim, mod, moduleName) {
138
138
  if (!trimedURL.includes(path.basename(parameters[0])) || decodedURL.includes(DOTDOTSLASH) || dynamicScanningFlag) {
139
139
  try {
140
140
  if (parameters[0].startsWith(DOTDOTSLASH)) {
141
- parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0];
141
+ parameters[0] = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0];
142
142
  } else if (parameters[0].startsWith(SLASHDOTDOT)) {
143
- parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0];
143
+ parameters[0] = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0];
144
144
  } else {
145
145
  parameters[0] = path.resolve(parameters[0]);
146
146
  }
@@ -347,7 +347,7 @@ function getCaseType(flag, filePath) {
347
347
  case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_EXCL:
348
348
  case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_SYNC:
349
349
  filePath = path.resolve(filePath);
350
- const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
350
+ const appPath = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath;
351
351
  const isInPath = isPathInside(filePath, appPath);
352
352
  if (typeof filePath === STRING && isInPath) {
353
353
  return EVENT_TYPE.FILE_INTEGRITY;
@@ -364,7 +364,7 @@ function getCaseType(flag, filePath) {
364
364
  */
365
365
  function getCase(filePath) {
366
366
  filePath = path.resolve(filePath);
367
- const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
367
+ const appPath = API.getSecAgent().applicationInfo.serverInfo.deployedApplications[0].deployedPath;
368
368
  const isInPath = isPathInside(filePath, appPath);
369
369
  if (typeof filePath === STRING && isInPath) {
370
370
  return EVENT_TYPE.FILE_INTEGRITY;
@@ -0,0 +1,301 @@
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 } = require('../../core/constants');
13
+ const logger = API.getLogger();
14
+ const semver = require('semver');
15
+ const grpcutils = require('../../core/grpc-utils');
16
+ let serverAddress;
17
+
18
+ module.exports.initialize = function initialize(shim, grpc, moduleName) {
19
+ logger.info('Instrumenting grpc', moduleName);
20
+ const grpcVersion = shim.require('./package.json').version;
21
+ logger.debug("grpc version:", grpcVersion);
22
+
23
+ if (semver.lt(grpcVersion, '1.4.0')) {
24
+ logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
25
+ return
26
+ }
27
+
28
+ if (semver.gte(grpcVersion, '1.8.0')) {
29
+ const resolvingCall = shim.require('./build/src/resolving-call')
30
+ shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
31
+ } else {
32
+ const callStream = shim.require('./build/src/call-stream')
33
+ shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
34
+ }
35
+
36
+ const server = shim.require('./build/src/server')
37
+ shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
38
+ shim.wrap(server.Server.prototype, 'register', wrapRegister)
39
+
40
+ //get all service
41
+ shim.wrap(server.Server.prototype, 'addService', wrapAddService);
42
+
43
+ //load package definition
44
+ const makeClient = shim.require('./build/src/make-client');
45
+ shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
46
+
47
+ }
48
+
49
+ module.exports.wrapStartResolve = function wrappedStartResolve(shim, resolvingCall) {
50
+ shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
51
+ }
52
+
53
+ module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
54
+ shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
55
+ }
56
+
57
+ module.exports.wrapServer = function wrappedServer(shim, server) {
58
+ const grpcVersion = shim.require('./package.json').version
59
+ logger.info("Instrumenting @grpc/grpc-js", grpcVersion)
60
+ if (semver.lt(grpcVersion, '1.4.0')) {
61
+ logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
62
+ return
63
+ }
64
+
65
+ shim.wrap(server.Server.prototype, 'register', wrapRegister)
66
+ shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
67
+ //get all service
68
+ shim.wrap(server.Server.prototype, 'addService', wrapAddService);
69
+ }
70
+
71
+ module.exports.wrapMakeClient = function wrapMakeClient(shim, makeClient) {
72
+ shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
73
+ }
74
+
75
+
76
+ /**
77
+ * Instruments grpc-js client by intercepting the function that
78
+ * initiates client requests. This handles all four types of client
79
+ * invocations: unary, client-streaming, server-streaming, and
80
+ * bidirectional streaming.
81
+ *
82
+ * @param {object} shim the generic shim to instrument with
83
+ * @param {Function} original the original function
84
+ * @returns {Function} the instrumented function
85
+ */
86
+ function wrapStart(shim, original) {
87
+ if (!shim.isFunction(original)) {
88
+ return original
89
+ }
90
+ return function wrappedStart() {
91
+ const channel = this.channel;
92
+ const authorityName = (channel.target && channel.target.path) || channel.getDefaultAuthority;
93
+ // in 1.8.0 this changed from methodName to method
94
+ const method = this.methodName || this.method;
95
+ const protocol = 'grpc:';
96
+ const url = `${protocol}//${authorityName}${method}`;
97
+ logger.debug("grpc url is:", url)
98
+ const request = requestManager.getRequest(shim);
99
+ let urlObj = {
100
+ "headers":
101
+ {
102
+ "host": authorityName
103
+ },
104
+ "method": method,
105
+ "path": method,
106
+ "protocol": protocol,
107
+ }
108
+ if (request) {
109
+ const traceObject = secUtils.getTraceObject(shim);
110
+ const secMetadata = securityMetaData.getSecurityMetaData(request, urlObj, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
111
+ const secEvent = API.generateSecEvent(secMetadata);
112
+ API.sendEvent(secEvent);
113
+ }
114
+
115
+ const res = original.apply(this, arguments);
116
+ return res;
117
+ }
118
+ }
119
+
120
+
121
+
122
+ function wrapAddService(shim, original) {
123
+ if (!shim.isFunction(original)) {
124
+ return original
125
+ }
126
+ return function wrappedbindAsync() {
127
+ const result = original.apply(this, arguments);
128
+ return result;
129
+ }
130
+ }
131
+
132
+ function wrapLoadPackageDefinition(shim, original) {
133
+ if (!shim.isFunction(original)) {
134
+ return original
135
+ }
136
+ return function wrappedbindAsync() {
137
+ let serviceObject = arguments[0];
138
+ const result = original.apply(this, arguments);
139
+ iterateNestedObjectForServiceLookup(result, serviceObject);
140
+ return result;
141
+ }
142
+ }
143
+
144
+
145
+
146
+
147
+ function bindAsyncWrapper(shim, original) {
148
+ if (!shim.isFunction(original)) {
149
+ return original
150
+ }
151
+ return function wrappedbindAsync() {
152
+ serverAddress = arguments[0];
153
+ shim.grpcServerAddress = serverAddress;
154
+ try {
155
+ const splittedData = serverAddress.split(":");
156
+ serverPort = splittedData[1];
157
+ shim.serverPort = parseInt(serverPort);
158
+ } catch (error) {
159
+
160
+ }
161
+ const result = original.apply(this, arguments);
162
+ return result;
163
+ }
164
+ }
165
+
166
+
167
+
168
+ /**
169
+ * Instruments the grpc-js server by intercepting the moment when
170
+ * server methods are registered from the method implementations
171
+ * provided to grpc-js. This handles all four types of server
172
+ * invocations: unary, client-streaming, server-streaming, and
173
+ * bidirectional streaming.
174
+ *
175
+ * @param {object} shim the web shim to instrument with
176
+ * @param {Function} original the original function
177
+ * @returns {Function} the instrumented function
178
+ */
179
+ function wrapRegister(shim, original) {
180
+ if (!shim.isFunction(original)) {
181
+ return original
182
+ }
183
+ return function wrappedRegister() {
184
+ wrapHandler(shim, arguments, '1');
185
+ const result = original.apply(this, arguments);
186
+ return result;
187
+ }
188
+ }
189
+
190
+
191
+ /**
192
+ * Wrapper to hook handler function
193
+ * @param {*} shim
194
+ * @param {*} mod
195
+ * @param {*} method
196
+ */
197
+ function wrapHandler(shim, mod, method) {
198
+ shim.wrap(mod, method, function wrapHandler(shim, fn) {
199
+ if (!shim.isFunction(fn)) {
200
+ return fn
201
+ }
202
+ return function wrappedHandler() {
203
+ const type = this.type;
204
+ const path = this.path;
205
+ wrapCallback(shim, arguments, '1');
206
+ addRequestData(shim, type, path, arguments[0]);
207
+ const result = fn.apply(this, arguments);
208
+ return result;
209
+
210
+ }
211
+ })
212
+ }
213
+
214
+ /**
215
+ * Wrapper to hook callback response
216
+ * @param {*} shim
217
+ * @param {*} mod
218
+ * @param {*} method
219
+ */
220
+ function wrapCallback(shim, mod, method) {
221
+ shim.wrap(mod, method, function wrapCallback(shim, fn) {
222
+ if (!shim.isFunction(fn)) {
223
+ return fn
224
+ }
225
+ return function wrappedCallback() {
226
+ const response = arguments[1];
227
+ const result = fn.apply(this, arguments);
228
+ return result;
229
+
230
+ }
231
+ })
232
+ }
233
+
234
+ /**
235
+ * Utility function to add grpc request data in request map
236
+ * @param {*} shim
237
+ * @param {*} type
238
+ * @param {*} path
239
+ * @param {*} args
240
+ */
241
+ function addRequestData(shim, type, path, args) {
242
+ const data = Object.assign({});
243
+ try {
244
+ const segment = shim.getActiveSegment();
245
+ if (segment && segment.transaction) {
246
+ data.method = path;
247
+ data.url = path;
248
+ data.uri = path;
249
+ data.headers = args.metadata.getMap();
250
+ if (data.headers && data.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
251
+ let bufferObj = Buffer.from(data.headers[NR_CSEC_FUZZ_REQUEST_ID], "base64");
252
+ let decodedString = bufferObj.toString("utf8");
253
+ data.headers[NR_CSEC_FUZZ_REQUEST_ID] = decodedString;
254
+ }
255
+ data.type = type;
256
+ data.protocol = 'grpc';
257
+ data.isGRPC = true;
258
+ data.body = JSON.stringify(args.request);
259
+ data.serverPort = shim.serverPort;
260
+ const transactionId = segment.transaction.id;
261
+ requestManager.setRequest(transactionId, data);
262
+ }
263
+ } catch (error) {
264
+ logger.error("Error occured while extracting grpc request", error);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Utilty to set grpc util Maps
270
+ * @param {*} serviceObject
271
+ */
272
+ function serviceUtil(serviceObject) {
273
+ let serviceValues = Object.values(serviceObject)[0];
274
+ for (const [key, value] of Object.entries(serviceValues)) {
275
+ let servObj = {
276
+ 'serviceName': serviceObject.serviceName,
277
+ 'originalName': value.originalName,
278
+ }
279
+ grpcutils.setMethod(value.path, servObj);
280
+ }
281
+ }
282
+
283
+ function iterateNestedObjectForServiceLookup(result, serviceObject) {
284
+ try {
285
+ for (const key in result) {
286
+ if (typeof result[key] === 'object' && result[key] !== 'null') {
287
+ iterateNestedObjectForServiceLookup(result[key], serviceObject); // Recurse into nested object
288
+ } else {
289
+ let value = result[key];
290
+ if (value && value.serviceName) {
291
+ if (!value.type && value.serviceName) {
292
+ grpcutils.setService(value.serviceName, serviceObject);
293
+ serviceUtil(value);
294
+ }
295
+ }
296
+ }
297
+ }
298
+ } catch (error) {
299
+ logger.error("Error while getting service definition", error);
300
+ }
301
+ }
@@ -5,7 +5,7 @@
5
5
 
6
6
  module.exports = initialize
7
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');
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, COMMA } = require('../../core/constants');
9
9
  const ARRAY_TYPE = 'Array';
10
10
  const STRING_TYPE = 'string';
11
11
  const BUFFER_TYPE = 'Buffer';
@@ -168,27 +168,38 @@ function parseFuzzheaders(requestData, transactionId) {
168
168
  try {
169
169
  const additionalData = fuzzheaders[NR_CSEC_FUZZ_REQUEST_ID].split(CSEC_SEP);
170
170
  logger.debug('AdditionalData:', additionalData);
171
- if (additionalData.length >= 7) {
172
- for (let i = 6; i < additionalData.length; i++) {
173
- let file = additionalData[i].trim();
174
- file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
175
- file = path.resolve(file);
176
- const parentDir = path.dirname(file);
177
- if (!isInvalid(parentDir) && isPathInside(parentDir, CSEC_HOME_TMP)) {
178
- try {
179
- if (!fs.existsSync(parentDir)) {
180
- secUtils.createPathIfNotExist(parentDir);
181
- } else {
182
- logger.debug(parentDir + ' Already Exists');
171
+ if (additionalData.length >= 8) {
172
+ let encryptedData = additionalData[6];
173
+ let hashVerifier = additionalData[7];
174
+ let decryptedData = secUtils.decryptData(encryptedData);
175
+ let verifiedHash = secUtils.hashVerifier(decryptedData, hashVerifier);
176
+ logger.debug("verifiedHash:", verifiedHash);
177
+ let filesToCreate = decryptedData.split(COMMA);
178
+ if (verifiedHash) {
179
+ logger.debug("Encrypted Data:", encryptedData);
180
+ logger.debug("Decrypted Data:", decryptedData)
181
+ logger.debug("fliesTocreate:",filesToCreate);
182
+ for (let i = 0; i < filesToCreate.length; i++) {
183
+ let file = filesToCreate[i].trim();
184
+ file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
185
+ file = path.resolve(file);
186
+ const parentDir = path.dirname(file);
187
+ if (!isInvalid(parentDir) && isPathInside(parentDir, CSEC_HOME_TMP)) {
188
+ try {
189
+ if (!fs.existsSync(parentDir)) {
190
+ secUtils.createPathIfNotExist(parentDir);
191
+ } else {
192
+ logger.debug(parentDir + ' Already Exists');
193
+ }
194
+ fs.closeSync(fs.openSync(file, 'w'));
195
+
196
+ requestData.tempFiles = [];
197
+ requestData.tempFiles.push(file);
198
+ requestManager.setRequest(transactionId, requestData);
199
+
200
+ } catch (error) {
201
+ logger.debug(error);
183
202
  }
184
- fs.closeSync(fs.openSync(file, 'w'));
185
-
186
- requestData.tempFiles = [];
187
- requestData.tempFiles.push(file);
188
- requestManager.setRequest(transactionId, requestData);
189
-
190
- } catch (error) {
191
- logger.debug(error);
192
203
  }
193
204
  }
194
205
  }
@@ -392,7 +403,7 @@ function responseHook(resp, req, shim) {
392
403
  const dynamicScanningFlag = policy.data ? (policy.data.vulnerabilityScan?.enabled && policy.data.vulnerabilityScan.iastScan.enabled) : false;
393
404
  const type = response.getHeader(CONTENT_TYPE);
394
405
  let isUnsupportedType = isUnsupportedContentType(type);
395
-
406
+
396
407
  if (request && (construct || dynamicScanningFlag) && !isUnsupportedType) {
397
408
  const args = [];
398
409
  args.push(construct);
@@ -400,7 +411,7 @@ function responseHook(resp, req, shim) {
400
411
  const traceObject = secUtils.getTraceObject(shim);
401
412
  const secMetadata = securityMetaData.getSecurityMetaData(request, args, traceObject, secUtils.getExecutionId(), EVENT_TYPE.REFLECTED_XSS, EVENT_CATEGORY.REFLECTED_XSS)
402
413
  const secEvent = API.generateSecEvent(secMetadata);
403
- secEvent.httpResponse = {};
414
+ secEvent.httpResponse = {};
404
415
  secEvent.httpResponse.contentType = response.getHeader(CONTENT_TYPE);
405
416
  API.sendEvent(secEvent);
406
417
  }
@@ -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
  })();
@@ -51,7 +51,6 @@ function setPolicyData(data) {
51
51
  policy.data = data;
52
52
  appInfoVersionUpdate(policy, data);
53
53
  initLogger.info("[STEP-7] => Received and applied policy/configuration", JSON.stringify(policy.data));
54
- logger.info('Applied policy is:', JSON.stringify(policy.data));
55
54
  if (NRAgent && NRAgent.config.security.detection) {
56
55
  logger.info('Security detection flags:', JSON.stringify(NRAgent.config.security.detection));
57
56
  }
@@ -12,7 +12,7 @@ const OS = require('os');
12
12
  const path = require('path');
13
13
  const checkDiskSpace = require('check-disk-space').default;
14
14
  const AgentStatus = require('../core/agent-status');
15
- const { CSEC_HOME, SLASH } = require('./sec-agent-constants');
15
+ const { CSEC_HOME, SLASH, EMPTY_STR } = require('./sec-agent-constants');
16
16
 
17
17
  const njsAgentConstants = require('./sec-agent-constants');
18
18
  const API = require('../../../nr-security-api');
@@ -107,7 +107,7 @@ function createPathIfNotExist(dir) {
107
107
  } catch (error) {
108
108
  logger.debug(dir + ' Not Created', error);
109
109
  const LogMessage = require('./LogMessage');
110
- const logMessage = new LogMessage.logMessage("SEVERE", 'Error in creating directory', __filename, error);
110
+ const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating directory', __filename, error);
111
111
  addLogEventtoBuffer(logMessage);
112
112
  }
113
113
  }
@@ -288,7 +288,7 @@ function removeOlderSnapshots() {
288
288
  } catch (error) {
289
289
  logger.debug(error);
290
290
  const LogMessage = require('./LogMessage');
291
- const logMessage = new LogMessage.logMessage("SEVERE", 'Error in processing snapshot files', __filename, error);
291
+ const logMessage = new LogMessage.logMessage("DEBUG", 'Error in processing snapshot files', __filename, error);
292
292
  addLogEventtoBuffer(logMessage);
293
293
  }
294
294
  }
@@ -445,6 +445,20 @@ function addLogEventtoBuffer(logMessage) {
445
445
  logger.debug(error);
446
446
  }
447
447
  }
448
+ /**
449
+ * Utility to get framework from APM
450
+ * @returns
451
+ */
452
+ function getFramework(){
453
+ let framework = EMPTY_STR;
454
+ try {
455
+ framework = NRAgent.environment.get('Framework')[0];
456
+ } catch (error) {
457
+ logger.debug("Unable to get framework");
458
+ }
459
+ return framework;
460
+ }
461
+
448
462
 
449
463
  module.exports = {
450
464
  getUUID,
@@ -469,5 +483,6 @@ module.exports = {
469
483
  getWSHealthStatus,
470
484
  setWSHealthStatus,
471
485
  addLogEventtoBuffer,
472
- getLogEvents
486
+ getLogEvents,
487
+ getFramework
473
488
  };
@@ -0,0 +1,112 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: New Relic Pre-Release
4
+ */
5
+ const logger = require('../logging').getLogger();
6
+ const grpcutils = require('../../../../instrumentation-security/core/grpc-utils');
7
+ const clientMap = new Map();
8
+ let grpc;
9
+ const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/sec-agent-constants');
10
+
11
+ /**
12
+ * Utility to get client object
13
+ * @param {*} config
14
+ * @returns
15
+ */
16
+ function getGPRCClient(config) {
17
+ let grpc = require("@grpc/grpc-js");
18
+ try {
19
+ const serviceName = grpcutils.getMethod(config.method).serviceName;
20
+
21
+ if (clientMap.has(serviceName)) {
22
+ return clientMap.get(serviceName);
23
+ }
24
+ const serviceObject = grpcutils.getService(serviceName);
25
+ const definition = grpc.loadPackageDefinition(serviceObject);
26
+ let service = getNestedValue(definition, serviceName)
27
+ if (service) {
28
+ logger.info("GRPC client service extracted successfully");
29
+ }
30
+ const client = new service(
31
+ "localhost:" + config.serverPort,
32
+ grpc.credentials.createInsecure()
33
+ );
34
+ if (!clientMap.has(serviceName)) {
35
+ clientMap.set(serviceName, client);
36
+ }
37
+ if(client){
38
+ logger.info("GRPC IAST client created successfully");
39
+ }
40
+ return client;
41
+ } catch (error) {
42
+ logger.error("error while creating client:", error);
43
+ }
44
+
45
+ }
46
+
47
+ /**
48
+ * Utility to fire unary requests
49
+ * @param {*} client
50
+ * @param {*} config
51
+ */
52
+ function fireUnaryRequest(client, config) {
53
+ try {
54
+ const methodObj = grpcutils.getMethod(config.requestURI);
55
+ if (methodObj && methodObj.originalName) {
56
+ let method = methodObj.originalName;
57
+ let metadata = new grpc.Metadata();
58
+ let bufferObj = Buffer.from(config.headers[NR_CSEC_FUZZ_REQUEST_ID], "utf8");
59
+ let base64String = bufferObj.toString("base64");
60
+ metadata.add(NR_CSEC_FUZZ_REQUEST_ID, base64String);
61
+ client[method](JSON.parse(config.data), metadata, (err, res) => {
62
+ if (err) {
63
+ logger.debug("Error occured:", err);
64
+ return
65
+ }
66
+
67
+ logger.info("Fuzz success: ", JSON.stringify(config));
68
+ return res;
69
+ })
70
+ }
71
+ else {
72
+ logger.debug("Not Found:", config.url);
73
+ }
74
+ } catch (error) {
75
+ logger.debug("Error while firing Unary request:", error);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * fire gRPC request based on provided config
81
+ * @param {*} config
82
+ * @returns
83
+ */
84
+ function fireRequest(config) {
85
+ try {
86
+ grpc = require("@grpc/grpc-js");
87
+ } catch (error) {
88
+ logger.debug("Unable to import grpc module", error);
89
+ }
90
+ let client = getGPRCClient(config);
91
+ fireUnaryRequest(client, config)
92
+ }
93
+
94
+
95
+ function getNestedValue(obj, targetKey) {
96
+ for (const key in obj) {
97
+ if (key === targetKey) {
98
+ return obj[key];
99
+ } else if (typeof obj[key] === 'object' && obj[key] !== null) {
100
+ const result = getNestedValue(obj[key], targetKey);
101
+ if (result !== undefined) {
102
+ return result;
103
+ }
104
+ }
105
+ }
106
+ return undefined; // Key not found
107
+ }
108
+
109
+
110
+ module.exports = {
111
+ fireRequest
112
+ };
@@ -31,6 +31,9 @@ function completedRequestsMapInit(id) {
31
31
 
32
32
  function addCompletedRequests(id, eventId) {
33
33
  let data = completedRequestsMap.get(id);
34
+ if(!data){
35
+ return;
36
+ }
34
37
  data.push(eventId);
35
38
  completedRequestsMap.set(id, data);
36
39
  }