@newrelic/security-agent 1.5.0 → 2.1.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 (43) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/THIRD_PARTY_NOTICES.md +48 -1
  3. package/lib/instrumentation-security/core/constants.js +8 -1
  4. package/lib/instrumentation-security/core/event-constants.js +5 -3
  5. package/lib/instrumentation-security/core/request-manager.js +40 -8
  6. package/lib/instrumentation-security/core/sec-utils.js +6 -5
  7. package/lib/instrumentation-security/hooks/director/nr-director.js +6 -6
  8. package/lib/instrumentation-security/hooks/express/nr-express.js +11 -8
  9. package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +6 -6
  10. package/lib/instrumentation-security/hooks/fs/nr-fs.js +11 -28
  11. package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +4 -4
  12. package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +7 -7
  13. package/lib/instrumentation-security/hooks/http/nr-http.js +137 -13
  14. package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +5 -5
  15. package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +4 -0
  16. package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +4 -0
  17. package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +4 -0
  18. package/lib/instrumentation-security/hooks/native/nr-childProcess.js +4 -0
  19. package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +4 -0
  20. package/lib/instrumentation-security/hooks/restify/nr-restify.js +7 -7
  21. package/lib/instrumentation-security/hooks/undici/nr-undici.js +4 -0
  22. package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +4 -0
  23. package/lib/nr-security-agent/index.js +38 -5
  24. package/lib/nr-security-agent/lib/core/Auth-headers.js +20 -0
  25. package/lib/nr-security-agent/lib/core/ExceptionReporting.js +1 -0
  26. package/lib/nr-security-agent/lib/core/Policy.js +0 -12
  27. package/lib/nr-security-agent/lib/core/agent.js +5 -1
  28. package/lib/nr-security-agent/lib/core/applicationinfo.js +2 -2
  29. package/lib/nr-security-agent/lib/core/commonUtils.js +141 -45
  30. package/lib/nr-security-agent/lib/core/connections/websocket/proxy-util.js +61 -0
  31. package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +34 -6
  32. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +36 -6
  33. package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +5 -1
  34. package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +7 -4
  35. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +32 -14
  36. package/lib/nr-security-agent/lib/core/event.js +2 -2
  37. package/lib/nr-security-agent/lib/core/health-check.js +8 -2
  38. package/lib/nr-security-agent/lib/core/sec-agent-constants.js +2 -3
  39. package/lib/nr-security-agent/lib/core/sec-util.js +40 -4
  40. package/lib/nr-security-agent/lib/core/websocket-client.js +35 -12
  41. package/lib/nr-security-api/index.js +47 -15
  42. package/package.json +10 -4
  43. package/lib/nr-security-agent/lib/core/statusUtils.js +0 -165
@@ -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, COMMA } = 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, SEMI_COLON, EQUAL, SECURE, HTTPONLY, SAMESITE, STRICT } = require('../../core/constants');
9
9
  const ARRAY_TYPE = 'Array';
10
10
  const STRING_TYPE = 'string';
11
11
  const BUFFER_TYPE = 'Buffer';
@@ -34,12 +34,14 @@ const semver = require('semver');
34
34
  const ExceptionReporting = require('../../../nr-security-agent/lib/core/ExceptionReporting');
35
35
 
36
36
  const CSEC_SEP = ':IAST:';
37
- const find = '{{NR_CSEC_VALIDATOR_HOME_TMP}}';
37
+ const sep = require('path').sep;
38
+ const find = `${sep}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
38
39
  const CSEC_HOME_TMP_CONST = new RegExp(find, 'g');
39
40
  let CSEC_HOME = NRAgent && NRAgent.config.newrelic_home ? NRAgent.config.newrelic_home : NRAgent && NRAgent.config.logging.filepath ? path.dirname(NRAgent.config.logging.filepath) : require('path').join(process.cwd());
40
41
  const CSEC_HOME_TMP = `${CSEC_HOME}/nr-security-home/tmp/language-agent/${process.env.applicationUUID}`
41
42
  let lastTransactionId = EMPTY_STRING;
42
43
  let uncaughtExceptionReportFlag = false;
44
+ const requestBodyLimit = 500;
43
45
 
44
46
 
45
47
  /**
@@ -90,6 +92,10 @@ function setupRequestMapCleanup() {
90
92
  * @param {*} moduleName
91
93
  */
92
94
  function outboundHook(shim, mod, method, moduleName) {
95
+ if (API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.ssrf) {
96
+ logger.warn('ssrf detection is disabled');
97
+ return;
98
+ }
93
99
  logger.debug(`Instrumenting ${moduleName}.${method}`);
94
100
  shim.wrap(mod, method, function makeRequestWrapper(shim, fn) {
95
101
  if (!shim.isFunction(fn)) {
@@ -172,8 +178,12 @@ function parseFuzzheaders(requestData, transactionId) {
172
178
  const additionalData = fuzzheaders[NR_CSEC_FUZZ_REQUEST_ID].split(CSEC_SEP);
173
179
  logger.debug('AdditionalData:', additionalData);
174
180
  if (additionalData.length >= 8) {
175
- let encryptedData = additionalData[6];
176
- let hashVerifier = additionalData[7];
181
+ let encryptedData = additionalData[6].trim();
182
+ let hashVerifier = additionalData[7].trim();
183
+
184
+ if (lodash.isEmpty(encryptedData) || lodash.isEmpty(hashVerifier)) {
185
+ return;
186
+ }
177
187
  let decryptedData = secUtils.decryptData(encryptedData);
178
188
  let verifiedHash = secUtils.hashVerifier(decryptedData, hashVerifier);
179
189
  logger.debug("verifiedHash:", verifiedHash);
@@ -184,6 +194,7 @@ function parseFuzzheaders(requestData, transactionId) {
184
194
  logger.debug("fliesTocreate:", filesToCreate);
185
195
  for (let i = 0; i < filesToCreate.length; i++) {
186
196
  let file = filesToCreate[i].trim();
197
+ file = decodeURIComponent(file);
187
198
  file = file.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
188
199
  file = path.resolve(file);
189
200
  const parentDir = path.dirname(file);
@@ -222,15 +233,15 @@ function parseFuzzheaders(requestData, transactionId) {
222
233
  function addRequestData(shim, request) {
223
234
  try {
224
235
  const data = Object.assign({});
225
- const segment = shim.getActiveSegment();
226
- if (segment && segment.transaction) {
236
+ const transaction = shim.tracer.getTransaction();
237
+ if (transaction) {
227
238
  data.protocol = (request.connection && request.connection.encrypted) ? 'https' : 'http';
228
239
  data.body = null;
229
240
  data.headers = request.headers;
230
241
  data.url = request.url;
231
242
  data.method = request.method;
232
243
  data.httpVersion = request.httpVersion;
233
- data.serverPort = segment.transaction.port;
244
+ data.serverPort = transaction.port;
234
245
  data.contextPath = '/';
235
246
  const queryObject = URL.parse(request.url, true).query;
236
247
  data.parameterMap = {};
@@ -244,7 +255,8 @@ function addRequestData(shim, request) {
244
255
  });
245
256
  }
246
257
  data.clientIP = requestIp.getClientIp(request);
247
- const transactionId = segment.transaction.id;
258
+ data.dataTruncated = false;
259
+ const transactionId = transaction.id;
248
260
  const storedRequest = requestManager.getRequestFromId(transactionId);
249
261
  lastTransactionId = transactionId;
250
262
  if (storedRequest && storedRequest.uri) {
@@ -284,7 +296,7 @@ function emitHook(shim, mod, moduleName) {
284
296
  const resp = arguments[2];
285
297
 
286
298
  if (arguments[0] == 'request') {
287
- if (NRAgent && NRAgent.config.security.detection.rxss.enabled) {
299
+ if (NRAgent && !NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.rxss && NRAgent.config.security.detection.rxss.enabled) {
288
300
  responseHook(resp, req, shim);
289
301
  }
290
302
 
@@ -356,14 +368,28 @@ function onDataHook(shim, mod) {
356
368
  return function onDataWrapper() {
357
369
  const chunk = arguments[0];
358
370
  const chunkType = typeof chunk;
359
- segment = shim.getActiveSegment()
371
+ const transaction = shim.tracer.getTransaction();
360
372
  if (chunkType === STRING_TYPE || chunkType === ARRAY_TYPE || chunkType === BUFFER_TYPE || chunkType === UINT8ARRAY_TYPE || chunkType === OBJECT_TYPE) {
361
373
  let data = chunk.toString();
362
- const transactionId = segment.transaction.id;
374
+ const transactionId = transaction.id;
363
375
  const requestData = requestManager.getRequestFromId(transactionId);
364
376
  if (requestData) {
365
377
  data = requestData.body ? requestData.body.concat(chunk.toString()) : chunk.toString();
366
- requestManager.updateRequestBody(shim, data);
378
+ requestManager.updateRequestBody(shim, data, false);
379
+ try {
380
+ const contentLength = Buffer.byteLength(data, 'utf8');
381
+ let bodyLimit = requestBodyLimit;
382
+ if (!isNaN(bodyLimit)) {
383
+ bodyLimit = bodyLimit * 1000;
384
+ if (contentLength && contentLength > bodyLimit) {
385
+ data = truncateStringToBytes(data, bodyLimit);
386
+ requestManager.updateRequestBody(shim, data, true);
387
+ }
388
+ }
389
+
390
+ } catch (error) {
391
+ logger.error("Error while truncating request body", error);
392
+ }
367
393
  }
368
394
 
369
395
  }
@@ -418,6 +444,11 @@ function responseHook(resp, req, shim) {
418
444
  } catch (error) {
419
445
  logger.debug("Error while reporting error", error);
420
446
  }
447
+ try {
448
+ secureCookieCheck(response, shim);
449
+ } catch (error) {
450
+ logger.debug("Error while generating secure cookie event");
451
+ }
421
452
 
422
453
  responseBodyCompute(response, arguments);
423
454
 
@@ -594,7 +625,7 @@ function deleteTempFileForIAST(file) {
594
625
  */
595
626
  process.on('uncaughtException', (err, origin) => {
596
627
  console.error(err);
597
- if(uncaughtExceptionReportFlag){
628
+ if (uncaughtExceptionReportFlag) {
598
629
  process.exit(1);
599
630
  }
600
631
  const request = requestManager.getRequestFromId(lastTransactionId);
@@ -606,3 +637,96 @@ process.on('uncaughtException', (err, origin) => {
606
637
  uncaughtExceptionReportFlag = true;
607
638
  });
608
639
 
640
+ /**
641
+ * utility to parse response headers for cookie check
642
+ * @param {*} headersString
643
+ * @returns
644
+ */
645
+ function responseRawHeaderParsing(headersString) {
646
+ let setCookieValue;
647
+ try {
648
+ const headersArray = headersString.split('\r\n');
649
+ for (const header of headersArray) {
650
+ if (header.toLowerCase().startsWith('set-cookie:')) {
651
+ setCookieValue = header.trim().replace('set-cookie:', '');
652
+ break;
653
+ }
654
+ }
655
+ } catch (error) {
656
+
657
+ }
658
+ return setCookieValue;
659
+ }
660
+
661
+ /**
662
+ * Utility to check insecure cookie settings
663
+ * @param {*} response
664
+ * @param {*} shim
665
+ */
666
+ function secureCookieCheck(response, shim) {
667
+ let cookieHeader = response.getHeader('set-cookie') ? response.getHeader('set-cookie') : responseRawHeaderParsing(response._header);
668
+ let cookieHeaderList = [];
669
+ if (!cookieHeader) {
670
+ return;
671
+ }
672
+ if (typeof cookieHeader === 'string') {
673
+ cookieHeaderList.push(cookieHeader)
674
+ }
675
+ else {
676
+ cookieHeaderList = cookieHeaderList.concat(cookieHeader);
677
+ }
678
+ let csec_request = requestManager.getRequest(shim);
679
+ try {
680
+ let parameters = [];
681
+ for (const cookieString of cookieHeaderList) {
682
+ if (cookieString) {
683
+ const keyValuePairs = cookieString.split(SEMI_COLON);
684
+ let params = {
685
+ "isHttpOnly": false,
686
+ "isSecure": false,
687
+ "isSameSiteStrict": false,
688
+ };
689
+ for (let i = 0; i < keyValuePairs.length; i++) {
690
+ const pair = keyValuePairs[i];
691
+ const [key, value] = pair.trim().split(EQUAL);
692
+ if (i == 0) {
693
+ params.name = key;
694
+ params.value = value;
695
+ }
696
+
697
+ if (key.toLowerCase() === SECURE) {
698
+ params.isSecure = true;
699
+ }
700
+ else if (key.toLowerCase() === HTTPONLY) {
701
+ params.isHttpOnly = true;
702
+ }
703
+ else if (key.toLowerCase() === SAMESITE && value.toLowerCase() === STRICT) {
704
+ params.isSameSiteStrict = true;
705
+ }
706
+ }
707
+ parameters.push(params);
708
+ }
709
+ }
710
+ const traceObject = secUtils.getTraceObject(shim);
711
+ const secMetadata = securityMetaData.getSecurityMetaData(csec_request, parameters, traceObject, secUtils.getExecutionId(), EVENT_TYPE.SECURE_COOKIE, EVENT_CATEGORY.SECURE_COOKIE)
712
+ const secEvent = API.generateSecEvent(secMetadata);
713
+ API.sendEvent(secEvent);
714
+ } catch (error) {
715
+ logger.debug("Error while generating secure cookie event:", error);
716
+ }
717
+
718
+ }
719
+
720
+ function truncateStringToBytes(inputString, maxBytes) {
721
+ if (!inputString || maxBytes <= 0) {
722
+ return EMPTY_STRING; // Return an empty string if input is invalid
723
+ }
724
+ const buffer = Buffer.from(inputString, UTF8);
725
+ if (buffer.length <= maxBytes) {
726
+ return inputString;
727
+ }
728
+ const truncatedBuffer = buffer.slice(0, maxBytes);
729
+ const truncatedString = truncatedBuffer.toString(UTF8);
730
+ return truncatedString;
731
+ }
732
+
@@ -62,14 +62,14 @@ function paramHook(shim, mod, moduleName) {
62
62
  shim.wrap(mod && mod.prototype, 'params', function wrapMatch(shim, fn) {
63
63
  logger.debug(`Instrumenting ${moduleName}.prototype.params`);
64
64
  return function wrappedMatch() {
65
- const segment = shim.getActiveSegment();
66
- if (segment && segment.transaction) {
67
- let request = requestManager.getRequestFromId(segment.transaction.id);
65
+ const transaction = shim.tracer.getTransaction();
66
+ if (transaction) {
67
+ let request = requestManager.getRequestFromId(transaction.id);
68
68
  try {
69
69
  const uri = this.path;
70
70
  if (request) {
71
71
  request.uri = uri;
72
- requestManager.setRequest(segment.transaction.id, request);
72
+ requestManager.setRequest(transaction.id, request);
73
73
  }
74
74
 
75
75
  } catch (error) {
@@ -97,7 +97,7 @@ function paramHook(shim, mod, moduleName) {
97
97
  }
98
98
  });
99
99
  }
100
- requestManager.setRequest(segment.transaction.id, request);
100
+ requestManager.setRequest(transaction.id, request);
101
101
  } catch (error) {
102
102
 
103
103
  }
@@ -19,6 +19,10 @@ const logger = API.getLogger();
19
19
  * @param {*} moduleName
20
20
  */
21
21
  function initialize(shim, mod, moduleName){
22
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.ldap_injection){
23
+ logger.warn('ldap_injection detection is disabled');
24
+ return;
25
+ }
22
26
  logger.info('Instrumenting '+ moduleName )
23
27
 
24
28
  if(moduleName==='ldapjs' || moduleName==='ldapts'){
@@ -32,6 +32,10 @@ module.exports = initialize;
32
32
  * @param {Shim} shim
33
33
  */
34
34
  function initialize(shim, mongodb, moduleName) {
35
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.nosql_injection){
36
+ logger.warn('nosql_injection detection is disabled');
37
+ return;
38
+ }
35
39
  logger.info(`Instrumenting ${moduleName}`);
36
40
  if (!mongodb) {
37
41
  return;
@@ -29,6 +29,10 @@ module.exports = initialize;
29
29
  */
30
30
  function initialize(shim, mysql, moduleName) {
31
31
  modName = moduleName;
32
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.sql_injection){
33
+ logger.warn('sql_injection detection is disabled');
34
+ return;
35
+ }
32
36
  logger.info("Instrumenting", moduleName);
33
37
  if (shim._moduleRoot.includes("mysql2")) {
34
38
  promiseInitialize(shim, mysql, moduleName);
@@ -13,6 +13,10 @@ const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
13
13
  const logger = API.getLogger();
14
14
 
15
15
  function initialize(shim, childProcess, moduleName) {
16
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.command_injection){
17
+ logger.warn('command_injection detection is disabled');
18
+ return;
19
+ }
16
20
  logger.info('Instrumenting child_process');
17
21
  execFileHook(shim, childProcess, moduleName);
18
22
  spawnHook(shim, childProcess, moduleName);
@@ -15,6 +15,10 @@ const logger = API.getLogger();
15
15
  const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
16
16
 
17
17
  module.exports = function initialize(shim, pgsql, moduleName) {
18
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.sql_injection){
19
+ logger.warn('sql_injection detection is disabled');
20
+ return;
21
+ }
18
22
  logger.info('Instrumenting '+ moduleName)
19
23
  // allows for native wrapping to not happen if not necessary
20
24
  // when env var is true
@@ -66,17 +66,17 @@ function initialize(shim, mod, moduleName) {
66
66
  * @param {*} req
67
67
  */
68
68
  function extractParams(shim, req) {
69
- const segment = shim.getActiveSegment();
70
- if (segment && segment.transaction) {
69
+ const transaction = shim.tracer.getTransaction();
70
+ if (transaction) {
71
71
 
72
- let request = requestManager.getRequestFromId(segment.transaction.id);
72
+ let request = requestManager.getRequestFromId(transaction.id);
73
73
  if (req.params && request) {
74
74
  try {
75
75
  Object.keys(req.params).forEach(function (key) {
76
76
  if (req.params[key]) {
77
77
  if (!request.parameterMap[key]) {
78
78
  request.parameterMap[key] = new Array(req.params[key].toString());
79
- requestManager.setRequest(segment.transaction.id, request);
79
+ requestManager.setRequest(transaction.id, request);
80
80
  }
81
81
  }
82
82
  });
@@ -91,7 +91,7 @@ function extractParams(shim, req) {
91
91
  Object.keys(query).forEach(function (key) {
92
92
  if (query[key]) {
93
93
  request.parameterMap[key] = new Array(query[key].toString());
94
- requestManager.setRequest(segment.transaction.id, request);
94
+ requestManager.setRequest(transaction.id, request);
95
95
  }
96
96
  });
97
97
  } catch (error) {
@@ -100,7 +100,7 @@ function extractParams(shim, req) {
100
100
  }
101
101
  if (req.route && req.route.path && request) {
102
102
  request.uri = req.route.path;
103
- requestManager.setRequest(segment.transaction.id, request);
103
+ requestManager.setRequest(transaction.id, request);
104
104
  }
105
105
  }
106
- }
106
+ }
@@ -24,6 +24,10 @@ const url = require('url');
24
24
  * @param {*} moduleName
25
25
  */
26
26
  function initialize(shim, mod, moduleName) {
27
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.ssrf){
28
+ logger.warn('ssrf detection is disabled');
29
+ return;
30
+ }
27
31
  logger.info('Instrumenting ' + moduleName);
28
32
  fetchHook(shim, mod, "fetch");
29
33
  let util = shim.require('./lib/core/util');
@@ -19,6 +19,10 @@ const logger = API.getLogger();
19
19
  * @param {*} moduleName
20
20
  */
21
21
  function initialize(shim, mod, moduleName){
22
+ if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.xpath_injection){
23
+ logger.warn('xpath_injection detection is disabled');
24
+ return;
25
+ }
22
26
  logger.info('Instrumenting '+ moduleName)
23
27
 
24
28
  if(moduleName==='xpath'){
@@ -17,7 +17,7 @@ const njsAgentConstants = require('./lib/core/sec-agent-constants');
17
17
  const NRLogger = API.newrelic.shim.logger.child({ component: 'security-agent' });
18
18
  NRLogger.info("Starting New Relic Node.js Security Agent ");
19
19
  try {
20
- if (NRAgent.config.security.enabled){
20
+ if (NRAgent.config.security.enabled) {
21
21
  require('../instrumentation-security');
22
22
  }
23
23
  } catch (error) {
@@ -117,11 +117,44 @@ function initialize() {
117
117
  if (NRAgent) {
118
118
  NRAgent.on('started', () => {
119
119
  NRLogger.info("Started New Relic Node.js Security Agent");
120
+ logger.info('NR Agent started with agent_run_id:', NRAgent.config.run_id, NRAgent.config.account_id);
121
+ if (Agent.getAgent().status.getStatus() != 'connecting') {
122
+ Agent.getAgent().delayed = false;
123
+ wsClient.obeyReconnect();
124
+ return;
125
+ }
120
126
  if (NRAgent.config.security.enabled || !NRAgent.config.high_security) {
121
- wsClient.initialize();
122
- logger.info('NR Agent started with agent_run_id:', NRAgent.config.run_id, NRAgent.config.account_id);
123
- Agent.getAgent().status.setStatus('connecting');
124
- Agent.setNRAgent(NRAgent);
127
+ let delayFromConfig = parseInt(NRAgent.config.security.scan_schedule.delay);
128
+ let delay = delayFromConfig;
129
+ if (isNaN(delay) || delay < 0) {
130
+ delay = 0;
131
+ }
132
+ NRAgent.config.security.scan_schedule.delay = delay;
133
+ const allowSampling = NRAgent.config.security.scan_schedule.always_sample_traces;
134
+
135
+ if (delay >= 0) {
136
+ logger.debug("IAST delay is set to:", delay);
137
+ logger.info("Security Agent delay scan time is set to:", commonUtils.getScheduledScanTime(delay))
138
+ Agent.getAgent().delayed = true;
139
+ setTimeout(() => {
140
+ Agent.getAgent().delayed = false;
141
+ logger.info("IAST scanning delay of %s minutes over", delay);
142
+ commonUtils.honourDurationConfiguration();
143
+ commonUtils.executeCronSchedule();
144
+ if (!allowSampling) {
145
+ wsClient.initialize();
146
+ Agent.getAgent().status.setStatus('connecting');
147
+ Agent.setNRAgent(NRAgent);
148
+ }
149
+ }, delay * 60000);
150
+ }
151
+ logger.info("Security Agent is running with config:", JSON.stringify(NRAgent.config.security));
152
+ if (allowSampling) {
153
+ wsClient.initialize();
154
+ Agent.getAgent().status.setStatus('connecting');
155
+ Agent.setNRAgent(NRAgent);
156
+ }
157
+
125
158
  }
126
159
  else {
127
160
  logger.warn("security.enabled flag is set to false");
@@ -11,6 +11,7 @@ const logger = logs.getLogger();
11
11
  const initLogger = logs.getInitLogger();
12
12
  const LinkingMetaData = require('./LinkingMetadata');
13
13
  const LogMessage = require('./LogMessage');
14
+ const lodash = require('lodash');
14
15
 
15
16
 
16
17
  /* eslint-disable camelcase */
@@ -37,12 +38,29 @@ function AuthHeaders() {
37
38
  authHeaders["NR-CSEC-IAST-DATA-TRANSFER-MODE"] = "PULL";
38
39
  authHeaders['NR-CSEC-ENTITY-NAME'] = metadata['entity.name'] ? metadata['entity.name'] : '';
39
40
  authHeaders['NR-CSEC-ENTITY-GUID'] = metadata['entity.guid'] ? metadata['entity.guid'] : '';
41
+ let instanceCount = parseInt(NRAgent.config.security.scan_controllers.scan_instance_count);
42
+ if (isNaN(instanceCount)) {
43
+ instanceCount = 0;
44
+ }
45
+ if (instanceCount < 0 || instanceCount > 1) {
46
+ instanceCount = 0;
47
+ }
48
+ authHeaders['NR-CSEC-IAST-SCAN-INSTANCE-COUNT'] = instanceCount ? instanceCount : 0
49
+ if ((NRAgent.config.security.iast_test_identifier && !lodash.isEmpty(NRAgent.config.security.iast_test_identifier)) || process.env.NEW_RELIC_SECURITY_IAST_TEST_IDENTIFIER) {
50
+ let iast_test_identifier = NRAgent.config.security.iast_test_identifier || process.env.NEW_RELIC_SECURITY_IAST_TEST_IDENTIFIER
51
+ authHeaders['NR-CSEC-IAST-TEST-IDENTIFIER'] = iast_test_identifier;
52
+ logger.debug("iast_test_identifier is:", iast_test_identifier);
53
+ authHeaders['NR-CSEC-IAST-SCAN-INSTANCE-COUNT'] = 1;
54
+ }
55
+ authHeaders['NR-CSEC-PROCESS-START-TIME'] = Date.now() - Math.floor(process.uptime() * 1000);
56
+ authHeaders['NR-CSEC-IGNORED-VUL-CATEGORIES'] = commonUtils.listSkipDetectionCategory();
40
57
  } catch (error) {
41
58
  logger.error("Exception in preparing auth headers:", error);
42
59
  const logMessage = new LogMessage.logMessage("SEVERE", 'Exception in preparing auth headers', __filename, error);
43
60
  commonUtils.addLogEventtoBuffer(logMessage);
44
61
  }
45
62
 
63
+
46
64
  return authHeaders;
47
65
  }
48
66
  AuthHeaders.prototype.constructor = AuthHeaders;
@@ -75,6 +93,8 @@ function getInstance() {
75
93
  return authHeaders;
76
94
  }
77
95
 
96
+
97
+
78
98
  module.exports = {
79
99
  getInstance
80
100
  };
@@ -89,6 +89,7 @@ function exceptionReporting(exception, request, responseCode) {
89
89
  this.timestamp = Date.now();
90
90
  this.httpRequest = request;
91
91
  this.httpRequest.route = request.uri ? request.uri : request.url;
92
+ this.httpRequest.requestURI = request.url.split('?')[0];
92
93
  this.exception = null;
93
94
  this.counter = 1;
94
95
  this.responseCode = responseCode ? responseCode : null
@@ -17,7 +17,6 @@ const logger = logs.getLogger();
17
17
  const initLogger = logs.getInitLogger();
18
18
  const jsonValidator = require('./jsonValidator');
19
19
  const LOG_MESSAGES = require('./sec-agent-constants').LOG_MESSAGES;
20
- const statusUtils = require('./statusUtils');
21
20
 
22
21
  function Policy() {
23
22
  this.data = {};
@@ -72,7 +71,6 @@ function setDefaultPolicy() {
72
71
  logger.info(LOG_MESSAGES.DEFAULT_POL_SET, JSON.stringify(policy.data));
73
72
  } catch (e) {
74
73
  logger.error(LOG_MESSAGES.UNABLE_SET_DEFAULT_POL, e);
75
- statusUtils.addErrortoBuffer(e);
76
74
  };
77
75
  }
78
76
 
@@ -90,16 +88,6 @@ function appInfoVersionUpdate(policy, data) {
90
88
  }
91
89
  }
92
90
 
93
- /**
94
- * Function to send updated policy to validator
95
- * @param {*} data
96
- */
97
- function sendUpdatedPolicy(data) {
98
- const wsClient = Agent.getAgent().client;
99
- let updatedPolicy = Object.assign({}, data);
100
- updatedPolicy.jsonName = 'lc-policy';
101
- wsClient.dispatcher(updatedPolicy);
102
- }
103
91
  module.exports = {
104
92
  getInstance,
105
93
  setPolicyData,
@@ -42,6 +42,7 @@ function Agent(applicationInfo, logger, client, status) {
42
42
  this.logger = logger;
43
43
  this.status = status;
44
44
  this.wsStatus = 0;
45
+ this.delayed = false;
45
46
  }
46
47
 
47
48
  Agent.prototype = {};
@@ -96,6 +97,7 @@ Agent.prototype.setAgentStatus = function setAgentStatus(status) {
96
97
  Agent.eventEmitter.emit(EVENTS.AGENT_STATUS_UPDATED_EVENT, status);
97
98
  };
98
99
 
100
+
99
101
  Agent.EVENTS = EVENTS;
100
102
 
101
103
  Agent.eventEmitter = new EventEmitter();
@@ -130,13 +132,15 @@ Agent.setNRAgent = function setNRAgent(agent) {
130
132
  }
131
133
  const CSECAgent = Agent.getAgent();
132
134
  if (CSECAgent) {
133
- CSECAgent.applicationInfo.entityGuid = NRAgent.config.entity_guid;
135
+ CSECAgent.applicationInfo.appEntityGuid = NRAgent.config.entity_guid;
136
+ CSECAgent.applicationInfo.appAccountId = NRAgent.config.account_id;
134
137
  CSECAgent.applicationInfo.linkingMetadata = LinkingMetaData.getLinkingMetadata();
135
138
  CSECAgent.applicationInfo.linkingMetadata.agentRunId = NRAgent.config.run_id;
136
139
  CSECAgent.setApplicationInfo(CSECAgent.applicationInfo);
137
140
  }
138
141
  };
139
142
 
143
+
140
144
  module.exports = {
141
145
  Agent
142
146
  };
@@ -90,10 +90,10 @@ function ApplicationInfo () {
90
90
  const k8sHost = isK8S();
91
91
  const ECSEnv = isECSEnv();
92
92
  const isLambda = IS_LAMBDA_ENV;
93
-
94
93
  this.jsonName = APPLICATION_INFO_KEYWORD;
95
94
  if (NRAgent && NRAgent.config) {
96
- this.entityGuid = NRAgent.config.entity_guid;
95
+ this.appEntityGuid = NRAgent.config.entity_guid;
96
+ this.appAccountId = NRAgent.config.account_id;
97
97
  }
98
98
  this.eventType = 'sec_appinfo';
99
99
  this.startTime = Date.now();