@newrelic/security-agent 1.1.0 → 1.2.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.
@@ -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
  }
@@ -200,55 +200,8 @@ newrelic.instrumentWebframework({
200
200
  })
201
201
 
202
202
 
203
- newrelic.instrument({
204
- type: 'conglomerate',
205
- moduleName: '@grpc/grpc-js/build/src/server',
206
- isEsm: true,
207
- onRequire: require('./hooks/grpc-js/nr-grpc').wrapServer,
208
- onError: function intrumentErrorHandler(err) {
209
- logger.error(err.message, err.stack)
210
- }
211
- })
212
-
213
- newrelic.instrument({
214
- type: 'conglomerate',
215
- moduleName: '@grpc/grpc-js/build/src/make-client',
216
- isEsm: true,
217
- onRequire: require('./hooks/grpc-js/nr-grpc').wrapMakeClient,
218
- onError: function intrumentErrorHandler(err) {
219
- logger.error(err.message, err.stack)
220
- }
221
- })
222
-
223
- newrelic.instrument({
224
- type: 'conglomerate',
225
- moduleName: '@grpc/grpc-js/build/src/resolving-call',
226
- isEsm: true,
227
- onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartResolve,
228
- onError: function intrumentErrorHandler(err) {
229
- logger.error(err.message, err.stack)
230
- }
231
- })
232
203
 
233
- newrelic.instrument({
234
- type: 'conglomerate',
235
- moduleName: '@grpc/grpc-js/build/src/call-stream',
236
- isEsm: true,
237
- onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartCall,
238
- onError: function intrumentErrorHandler(err) {
239
- logger.error(err.message, err.stack)
240
- }
241
- })
242
204
 
243
- newrelic.instrument({
244
- type: 'conglomerate',
245
- moduleName: '@grpc/grpc-js',
246
- isEsm: true,
247
- onRequire: require('./hooks/grpc-js/nr-grpc').initialize,
248
- onError: function intrumentErrorHandler(err) {
249
- logger.error(err.message, err.stack)
250
- }
251
- })
252
205
 
253
206
 
254
207
 
@@ -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');
@@ -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
  };
@@ -7,7 +7,6 @@
7
7
  const stringify = require('fast-safe-stringify');
8
8
 
9
9
  const restClient = require('../../restclient');
10
- const grpcClient = require('../../grpcClient');
11
10
  const { Agent } = require('../../../agent');
12
11
  const { FuzzFailEvent } = require('../../../FuzzFailEvent');
13
12
  const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP, SEVERE } = require('../../../sec-agent-constants');
@@ -21,6 +20,9 @@ const LogMessage = require('../../../LogMessage');
21
20
  require('dns').setDefaultResultOrder('ipv4first')
22
21
  const PolicyManager = require('../../../Policy');
23
22
 
23
+ const {
24
+ IS_LAMBDA_ENV,
25
+ AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER } = require('../../../sec-agent-constants');
24
26
  const statusUtils = require('../../../statusUtils');
25
27
 
26
28
  const find = `${SLASH}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
@@ -123,16 +125,13 @@ function handler(json) {
123
125
  function handleFuzzRequest(fuzzDetails) {
124
126
  const { fuzzRequest } = fuzzDetails;
125
127
  try {
126
- if (fuzzRequest.protocol == 'grpc') {
127
- const config = parseGRPCRequestToFuzz(fuzzRequest);
128
- config.headers['nr-csec-parent-id'] = fuzzRequest.id;
129
- IASTUtil.completedRequestsMapInit(fuzzRequest.id);
130
- grpcClient.fireRequest(config);
131
- if (fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
132
- logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
133
- }
134
-
135
- IASTUtil.removePendingRequestId(fuzzRequest.id);
128
+ if (IS_LAMBDA_ENV) {
129
+ const qualifiedARN = Agent.getAgent().applicationInfo.applicationUUID;
130
+ const splitArn = String.prototype.split.call(qualifiedARN, ':');
131
+ const arn = splitArn.slice(0, splitArn.length - 1).join(':');
132
+ const ver = process.env[AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER];
133
+ logger.info('Invoking Lambda:: ARN: ' + arn);
134
+ logger.info('Invoking Lambda:: Version: ' + ver);
136
135
 
137
136
  } else {
138
137
  const config = parseAxiosHttpRequestToFuzz(fuzzRequest);
@@ -154,6 +153,7 @@ function handleFuzzRequest(fuzzDetails) {
154
153
 
155
154
 
156
155
  logger.info('Firing http request:: URL: ' + config.url);
156
+
157
157
  const response = restClient.fireRequest(config);
158
158
  handleFuzzResponse(response, fuzzDetails);
159
159
  }
@@ -210,26 +210,6 @@ function parseAxiosHttpRequestToFuzz(requestObject) {
210
210
  };
211
211
  }
212
212
 
213
- /**
214
- * Parses the passed fuzz request object to
215
- * Axios config.
216
- *
217
- * @param {JSON} requestObject
218
- */
219
- function parseGRPCRequestToFuzz(requestObject) {
220
- let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
221
- let host = serverName + COLON + requestObject.serverPort
222
- return {
223
- url: requestObject.protocol + COLON_SLASH_SLASH + host + requestObject.url,
224
- requestURI: requestObject.requestURI,
225
- protocol: requestObject.protocol,
226
- serverPort: requestObject.serverPort,
227
- method: requestObject.method,
228
- data: requestObject.body,
229
- headers: requestObject.headers,
230
- timeout: 5000,
231
- };
232
- }
233
213
 
234
214
 
235
215
  /**
@@ -111,6 +111,7 @@ module.exports = {
111
111
  SEVERE: 'SEVERE',
112
112
  HIGH: 'HIGH',
113
113
  CRITICAL: 'CRITICAL',
114
+ HYPHEN: '-',
114
115
 
115
116
  LOG_MESSAGES: {
116
117
  LOADED_CSEC_ENVS_MSG: '[STEP-1][COMPLETE][env] Environment Information Gathering Done.',
@@ -165,7 +166,7 @@ module.exports = {
165
166
  ERROR_WHILE_SEND_EVENT: 'ERROR While Sending Event: {}',
166
167
  WS_CONNECTED: '[STEP-4][COMPLETE][WS] Connected To Prevent-web Service',
167
168
  SENDING_APPINFO: '[APP_INFO] Sending Application Info To Prevent-web Service: ',
168
- SENDING_APPINFO_COMPLETE: '[COMPLETE][APP_INFO] Application info sent to Prevent-Web service :',
169
+ SENDING_APPINFO_COMPLETE: '[STEP-3][COMPLETE][APP_INFO] Application info sent to Prevent-Web service :',
169
170
  APPLY_INSTRUMENTATION: '[STEP-6][BEGIN][instrumentation] Applying Instrumentation',
170
171
  AVAIL_DISK: 'Available Disk Space Is: ',
171
172
  AVAIL_DISK_ERROR: 'Insufficient Disk Space Available To The Location %s Is : %s',
@@ -9,7 +9,7 @@ const { SecEvent } = require('./event');
9
9
  const logs = require('./logging');
10
10
  const { Agent } = require('./agent');
11
11
  const shaUtil = require('./sha-size-util');
12
- const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE } = require('./sec-agent-constants');
12
+ const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE, HYPHEN } = require('./sec-agent-constants');
13
13
  const API = require('../../../nr-security-api');
14
14
  const NRAgent = API.getNRAgent();
15
15
  const HC = require('./health-check');
@@ -83,6 +83,7 @@ function generateSecEvent(securityMetadata) {
83
83
  }
84
84
 
85
85
  let apiId = shaUtil.getSHA256ForData(traceObject.stacktrace.join('|') + uri);
86
+ apiId = securityMetadata.eventType + HYPHEN + apiId;
86
87
  const agentModule = Agent.getAgent();
87
88
  const metaData = {};
88
89
  if (traceObject.sourceDetails && traceObject.sourceDetails.evalObj) {
@@ -23,10 +23,11 @@ const logs = require('./logging');
23
23
  const commonUtils = require('./commonUtils');
24
24
  const logger = logs.getLogger();
25
25
  const statusFile = njsAgentConstants.STATUS_LOG_FILE;
26
+ const sep = require('path').sep;
26
27
 
27
28
  const statusTemplate = 'Snapshot timestamp: : %s\n' +
28
29
  `CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
29
- `SEC_HOME: ${njsAgentConstants.CSEC_HOME}\n` +
30
+ `CSEC_HOME: ${njsAgentConstants.CSEC_HOME}${sep}nr-security-home${sep}\n` +
30
31
  `Agent location: ${AGENT_DIR}\n` +
31
32
  `Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +
32
33
  `Process title: ${process.title}\n` +
@@ -35,10 +36,10 @@ const statusTemplate = 'Snapshot timestamp: : %s\n' +
35
36
  `Current working directory: ${process.cwd()}\n` +
36
37
  `Agent mode: ${commonUtils.getCSECmode()}\n` +
37
38
  'Application server: %s\n' +
38
- 'Application Framework: %s\n' +
39
+ `Application Framework: %s\n` +
39
40
  `Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
40
- 'Instrumentation successful:\n' +
41
- 'Tracking loaded modules in the application:\n' +
41
+ 'Instrumentation successful\n' +
42
+ 'Tracking loaded modules in the application\n' +
42
43
  'Policy applied successfully. Policy version is: %s\n' +
43
44
  'Started Health Check for Agent\n' +
44
45
  'Started Inbound and Outbound monitoring \n' +
@@ -83,7 +84,7 @@ function getFormattedData() {
83
84
  const appInfo = Agent.applicationInfo;
84
85
  const deployedApplications = appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications;
85
86
  const appLoc = deployedApplications[0].deployedPath;
86
- const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, njsAgentConstants.FRAMEWORK, commonUtils.getWSHealthStatus(), appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
87
+ const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, commonUtils.getFramework(), commonUtils.getWSHealthStatus(), appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
87
88
  return formattedSnapshot;
88
89
  }
89
90
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
- "jsonVersion": "1.1.1",
6
+ "jsonVersion": "1.2.0",
7
7
  "contributors": [
8
8
  {
9
9
  "name": "Sumit Suthar",
@@ -58,8 +58,6 @@
58
58
  "ws": "^8.14.2"
59
59
  },
60
60
  "devDependencies": {
61
- "@grpc/grpc-js": "^1.9.5",
62
- "@grpc/proto-loader": "^0.7.10",
63
61
  "@hapi/hapi": "^21.3.0",
64
62
  "@koa/router": "^12.0.0",
65
63
  "@newrelic/newrelic-oss-cli": "^0.1.2",
@@ -1,53 +0,0 @@
1
- /*
2
- * Copyright 2023 New Relic Corporation. All rights reserved.
3
- * SPDX-License-Identifier: New Relic Pre-Release
4
- */
5
-
6
-
7
- const grpcServiceMap = new Map();
8
- const grpcMethodMap = new Map();
9
- /**
10
- * Utility to set service based on key value pair
11
- * @param {*} key
12
- * @param {*} value
13
- */
14
- function setService(key, value){
15
- grpcServiceMap.set(key,value);
16
- }
17
-
18
- /**
19
- * Utility to get service corresponding to key
20
- * @param {*} key
21
- * @returns
22
- */
23
- function getService(key){
24
- return grpcServiceMap.get(key);
25
- }
26
-
27
- /**
28
- * Utility to set method object
29
- * @param {*} key
30
- * @param {*} value
31
- */
32
- function setMethod(key, value){
33
- grpcMethodMap.set(key,value);
34
- }
35
-
36
- /**
37
- * Utility to get method object
38
- * @param {*} key
39
- * @returns
40
- */
41
- function getMethod(key){
42
- return grpcMethodMap.get(key);
43
- }
44
-
45
- module.exports={
46
- setService,
47
- getService,
48
- grpcServiceMap,
49
- setMethod,
50
- getMethod,
51
- grpcMethodMap
52
-
53
- }