@newrelic/security-agent 0.2.1 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ### v0.4.0 (2023-10-27)
2
+ #### Features
3
+ * Added event stats for RASP, IAST and exit events in healthcheck.
4
+ #### Miscellaneous chores
5
+ * Deps: Updated uuid to v9.0.1 and @aws-sdk/client-lambda to v3.405.0
6
+ * Upgraded axios to 1.6.0 to resolve CVE-2023-45857
7
+ #### Continuous integration
8
+ * Removed Node.js 14.x from CI.
9
+ * Added should_skip flag to skip CI based on label.
10
+
11
+ ### v0.3.0 (2023-09-25)
12
+ #### Features
13
+ * Last leg acknowledgement in IAST scanning.
14
+ #### Bug Fixes
15
+ * Fix for mysql query params in security events.
16
+ * Logging update for default log level and status file flags.
17
+ * Update in IAST batch size processing.
18
+ * Disable instrumentation when security enabled flag is set to false
19
+ #### Miscellaneous Chores
20
+ * Readme update
21
+
1
22
  ### v0.2.1 (2023-08-07)
2
23
  #### Bug Fixes
3
24
  * Pinned check-disk-space to v3.3.1 to support Node.js v14
package/README.md CHANGED
@@ -42,7 +42,7 @@ The `@newrelic/security-agent` must be explicitly enabled in order to perform IA
42
42
  The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instrumentation will be of help. We particularly recommend the [tutorials](https://newrelic.github.io/node-newrelic/tutorial-Instrumentation-Basics.html) and the [shim API](https://newrelic.github.io/node-newrelic/Shim.html) documentation.
43
43
 
44
44
  ### Supported Node.js Versions
45
- - Node version 14.x, 16.x and 18.x
45
+ - Node version 16.x, 18.x and 20.x
46
46
 
47
47
  ### Supported Modules
48
48
 
@@ -56,7 +56,7 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
56
56
  - [pg](https://www.npmjs.com/package/pg)(7.x and above)
57
57
  - [mongodb](https://www.npmjs.com/package/mongodb)(2.x, 3.x and 4.x)
58
58
  - [express](https://www.npmjs.com/package/express)(4.x and above)
59
- - [@koa/router](https://www.npmjs.com/package/@koa/router) ((9.x and above)
59
+ - [@koa/router](https://www.npmjs.com/package/@koa/router) (9.x and above)
60
60
  - [koa-router](https://www.npmjs.com/package/koa-router)(9.x and above)
61
61
  - [@hapi/hapi](https://www.npmjs.com/package/@hapi/hapi)(19.x and above)
62
62
  - [fastify](https://www.npmjs.com/package/fastify)(2.x, 3.x and 4.x)
@@ -75,7 +75,7 @@ code, the source code can be found at [https://github.com/newrelic/csec-node-age
75
75
 
76
76
  ### @aws-sdk/client-lambda
77
77
 
78
- This product includes source derived from [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3) ([v3.379.1](https://github.com/aws/aws-sdk-js-v3/tree/v3.379.1)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.379.1/LICENSE):
78
+ This product includes source derived from [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3) ([v3.435.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.435.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.435.0/LICENSE):
79
79
 
80
80
  ```
81
81
  Apache License
@@ -284,28 +284,16 @@ This product includes source derived from [@aws-sdk/client-lambda](https://githu
284
284
 
285
285
  ### axios
286
286
 
287
- This product includes source derived from [axios](https://github.com/axios/axios) ([v0.21.4](https://github.com/axios/axios/tree/v0.21.4)), distributed under the [MIT License](https://github.com/axios/axios/blob/v0.21.4/LICENSE):
287
+ This product includes source derived from [axios](https://github.com/axios/axios) ([v1.6.0](https://github.com/axios/axios/tree/v1.6.0)), distributed under the [MIT License](https://github.com/axios/axios/blob/v1.6.0/LICENSE):
288
288
 
289
289
  ```
290
- Copyright (c) 2014-present Matt Zabriskie
290
+ # Copyright (c) 2014-present Matt Zabriskie & Collaborators
291
291
 
292
- Permission is hereby granted, free of charge, to any person obtaining a copy
293
- of this software and associated documentation files (the "Software"), to deal
294
- in the Software without restriction, including without limitation the rights
295
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
296
- copies of the Software, and to permit persons to whom the Software is
297
- furnished to do so, subject to the following conditions:
292
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
298
293
 
299
- The above copyright notice and this permission notice shall be included in
300
- all copies or substantial portions of the Software.
294
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
301
295
 
302
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
303
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
304
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
305
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
306
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
307
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
308
- THE SOFTWARE.
296
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
309
297
 
310
298
  ```
311
299
 
@@ -790,7 +778,7 @@ THE SOFTWARE.
790
778
 
791
779
  ### uuid
792
780
 
793
- This product includes source derived from [uuid](https://github.com/uuidjs/uuid) ([v9.0.0](https://github.com/uuidjs/uuid/tree/v9.0.0)), distributed under the [MIT License](https://github.com/uuidjs/uuid/blob/v9.0.0/LICENSE.md):
781
+ This product includes source derived from [uuid](https://github.com/uuidjs/uuid) ([v9.0.1](https://github.com/uuidjs/uuid/tree/v9.0.1)), distributed under the [MIT License](https://github.com/uuidjs/uuid/blob/v9.0.1/LICENSE.md):
794
782
 
795
783
  ```
796
784
  The MIT License (MIT)
@@ -75,6 +75,9 @@ function wrapExpress4(shim, express) {
75
75
  * @param {*} req
76
76
  */
77
77
  function extractParams(shim, req) {
78
+ if (!requestManager.getRequest(shim)) {
79
+ secUtils.addRequestData(shim, req);
80
+ }
78
81
  const segment = shim.getActiveSegment();
79
82
  if (segment && segment.transaction) {
80
83
  let request = requestManager.getRequestFromId(segment.transaction.id);
@@ -36,7 +36,7 @@ const semver = require('semver');
36
36
  const CSEC_SEP = ':IAST:';
37
37
  const find = '{{NR_CSEC_VALIDATOR_HOME_TMP}}';
38
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());
39
+ 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
40
  const CSEC_HOME_TMP = `${CSEC_HOME}/nr-security-home/tmp/language-agent/${process.env.applicationUUID}`
41
41
 
42
42
  /**
@@ -177,11 +177,16 @@ function wrapQueryable(shim, queryable, isPoolQuery) {
177
177
  * @returns
178
178
  */
179
179
  function containsOnlyStrings(list) {
180
- for (let i = 0; i < list.length; i++) {
181
- if (typeof list[i] !== 'string') {
182
- return false;
180
+ try {
181
+ for (let i = 0; i < list.length; i++) {
182
+ if (typeof list[i] !== 'string') {
183
+ list[i] = list[i].toString();
184
+ }
183
185
  }
186
+ } catch (error) {
187
+ return false;
184
188
  }
189
+
185
190
  return true;
186
191
  }
187
192
 
@@ -202,6 +207,13 @@ function extractQueryArgs(shim, args) {
202
207
  } else {
203
208
  // query(opts [, values], callback)
204
209
  rawQuery = args[0].sql;
210
+ try {
211
+ if (args[0].values && typeof args[0].values == 'object') {
212
+ param = Object.values(args[0].values)
213
+ }
214
+ } catch (error) {
215
+ logger.warn("Not able to extract params for mysql")
216
+ }
205
217
  }
206
218
  // Then determine the query values and callback parameters.
207
219
  if (shim.isArray(args[1])) {
@@ -210,9 +222,9 @@ function extractQueryArgs(shim, args) {
210
222
  }
211
223
  else if (args[1] && typeof args[1] == 'object') {
212
224
  param = args[1];
213
- param = Object.values(param)
225
+ param = Object.values(param)
214
226
  }
215
- if(!containsOnlyStrings(param)){
227
+ if (!containsOnlyStrings(param)) {
216
228
  param = [];
217
229
  }
218
230
  return {
@@ -12,6 +12,7 @@ logger.info("Started instrumentation using shimmer module");
12
12
 
13
13
  newrelic.instrument({
14
14
  moduleName: 'child_process',
15
+ isEsm: true,
15
16
  onRequire: require('./hooks/native/nr-childProcess'),
16
17
  onError: function intrumentErrorHandler(err) {
17
18
  logger.error(err.message, err.stack)
@@ -19,6 +20,7 @@ newrelic.instrument({
19
20
  })
20
21
  newrelic.instrument({
21
22
  moduleName: 'http',
23
+ isEsm: true,
22
24
  onRequire: require('./hooks/http/nr-http'),
23
25
  onError: function intrumentErrorHandler(err) {
24
26
  logger.error(err.message, err.stack)
@@ -27,6 +29,7 @@ newrelic.instrument({
27
29
 
28
30
  newrelic.instrument({
29
31
  moduleName: 'https',
32
+ isEsm: true,
30
33
  onRequire: require('./hooks/http/nr-http'),
31
34
  onError: function intrumentErrorHandler(err) {
32
35
  logger.error(err.message, err.stack)
@@ -35,6 +38,7 @@ newrelic.instrument({
35
38
 
36
39
  newrelic.instrument({
37
40
  moduleName: 'fs',
41
+ isEsm: true,
38
42
  onRequire: require('./hooks/fs/nr-fs'),
39
43
  onError: function intrumentErrorHandler(err) {
40
44
  logger.error(err.message, err.stack)
@@ -43,6 +47,7 @@ newrelic.instrument({
43
47
 
44
48
  newrelic.instrumentWebframework({
45
49
  moduleName: 'express',
50
+ isEsm: true,
46
51
  onRequire: require('./hooks/express/nr-express'),
47
52
  onError: function intrumentErrorHandler(err) {
48
53
  logger.error(err.message, err.stack)
@@ -51,6 +56,7 @@ newrelic.instrumentWebframework({
51
56
 
52
57
  newrelic.instrumentDatastore({
53
58
  moduleName: 'mysql',
59
+ isEsm: true,
54
60
  onRequire: require('./hooks/mysql/nr-mysql'),
55
61
  onError: function intrumentErrorHandler(err) {
56
62
  logger.error(err.message, err.stack)
@@ -59,6 +65,7 @@ newrelic.instrumentDatastore({
59
65
 
60
66
  newrelic.instrumentDatastore({
61
67
  moduleName: 'mysql2',
68
+ isEsm: true,
62
69
  onRequire: require('./hooks/mysql/nr-mysql'),
63
70
  onError: function intrumentErrorHandler(err) {
64
71
  logger.error(err.message, err.stack)
@@ -67,6 +74,7 @@ newrelic.instrumentDatastore({
67
74
 
68
75
  newrelic.instrumentDatastore({
69
76
  moduleName: 'mongodb',
77
+ isEsm: true,
70
78
  onRequire: require('./hooks/mongodb/nr-mongodb'),
71
79
  onError: function intrumentErrorHandler(err) {
72
80
  logger.error(err.message, err.stack)
@@ -75,6 +83,7 @@ newrelic.instrumentDatastore({
75
83
 
76
84
  newrelic.instrument({
77
85
  moduleName: 'ldapjs',
86
+ isEsm: true,
78
87
  onRequire: require('./hooks/ldap/nr-ldap'),
79
88
  onError: function intrumentErrorHandler(err) {
80
89
  logger.error(err.message, err.stack)
@@ -83,6 +92,7 @@ newrelic.instrument({
83
92
 
84
93
  newrelic.instrument({
85
94
  moduleName: 'ldapts',
95
+ isEsm: true,
86
96
  onRequire: require('./hooks/ldap/nr-ldap'),
87
97
  onError: function intrumentErrorHandler(err) {
88
98
  logger.error(err.message, err.stack)
@@ -92,6 +102,7 @@ newrelic.instrument({
92
102
 
93
103
  newrelic.instrument({
94
104
  moduleName: 'xpath',
105
+ isEsm: true,
95
106
  onRequire: require('./hooks/xpath/nr-xpath'),
96
107
  onError: function intrumentErrorHandler(err) {
97
108
  logger.error(err.message, err.stack)
@@ -99,6 +110,7 @@ newrelic.instrument({
99
110
  })
100
111
  newrelic.instrument({
101
112
  moduleName: 'xpath.js',
113
+ isEsm: true,
102
114
  onRequire: require('./hooks/xpath/nr-xpath'),
103
115
  onError: function intrumentErrorHandler(err) {
104
116
  logger.error(err.message, err.stack)
@@ -107,6 +119,7 @@ newrelic.instrument({
107
119
 
108
120
  newrelic.instrumentDatastore({
109
121
  moduleName: 'pg',
122
+ isEsm: true,
110
123
  onRequire: require('./hooks/postgres/nr-postgres'),
111
124
  onError: function intrumentErrorHandler(err) {
112
125
  logger.error(err.message, err.stack)
@@ -115,6 +128,7 @@ newrelic.instrumentDatastore({
115
128
 
116
129
  newrelic.instrumentWebframework({
117
130
  moduleName: 'fastify',
131
+ isEsm: true,
118
132
  onRequire: require('./hooks/fastify/nr-fastify'),
119
133
  onError: function intrumentErrorHandler(err) {
120
134
  logger.error(err.message, err.stack)
@@ -124,6 +138,7 @@ newrelic.instrumentWebframework({
124
138
 
125
139
  newrelic.instrumentWebframework({
126
140
  moduleName: 'restify',
141
+ isEsm: true,
127
142
  onRequire: require('./hooks/restify/nr-restify'),
128
143
  onError: function intrumentErrorHandler(err) {
129
144
  logger.error(err.message, err.stack)
@@ -132,6 +147,7 @@ newrelic.instrumentWebframework({
132
147
 
133
148
  newrelic.instrumentWebframework({
134
149
  moduleName: '@koa/router',
150
+ isEsm: true,
135
151
  onRequire: require('./hooks/koa/@koa/nr-@koa-router'),
136
152
  onError: function intrumentErrorHandler(err) {
137
153
  logger.error(err.message, err.stack)
@@ -140,6 +156,7 @@ newrelic.instrumentWebframework({
140
156
 
141
157
  newrelic.instrumentWebframework({
142
158
  moduleName: 'koa-router',
159
+ isEsm: true,
143
160
  onRequire: require('./hooks/koa/@koa/nr-@koa-router'),
144
161
  onError: function intrumentErrorHandler(err) {
145
162
  logger.error(err.message, err.stack)
@@ -148,6 +165,7 @@ newrelic.instrumentWebframework({
148
165
 
149
166
  newrelic.instrumentWebframework({
150
167
  moduleName: '@hapi/hapi',
168
+ isEsm: true,
151
169
  onRequire: require('./hooks/hapi/nr-hapi'),
152
170
  onError: function intrumentErrorHandler(err) {
153
171
  logger.error(err.message, err.stack)
@@ -156,6 +174,7 @@ newrelic.instrumentWebframework({
156
174
 
157
175
  newrelic.instrumentWebframework({
158
176
  moduleName: 'director',
177
+ isEsm: true,
159
178
  onRequire: require('./hooks/director/nr-director'),
160
179
  onError: function intrumentErrorHandler(err) {
161
180
  logger.error(err.message, err.stack)
@@ -159,6 +159,10 @@ function getProcCMDLineSanitized(applicationInfoModule) {
159
159
  }
160
160
 
161
161
  (() => {
162
+ if(!NRAgent.config.security.enabled){
163
+ logger.warn("security.enabled flag is set to false");
164
+ return;
165
+ }
162
166
  if (initialize()) {
163
167
  require('../instrumentation-security');
164
168
  initLogger.info("[STEP-6] => Application instrumentation applied successfully");
@@ -21,7 +21,7 @@ function AuthHeaders() {
21
21
  const NRAgent = API.getNRAgent();
22
22
  authHeaders = {}
23
23
  authHeaders['NR-CSEC-CONNECTION-TYPE'] = 'LANGUAGE_COLLECTOR';
24
- authHeaders['NR-AGENT-RUN-TOKEN'] = NRAgent.config.run_id;
24
+ authHeaders['NR-AGENT-RUN-TOKEN'] = NRAgent.config.run_id;
25
25
  const COLLECTOR_VERSION = appInfo.collectorVersion || '';
26
26
  authHeaders['NR-CSEC-VERSION'] = COLLECTOR_VERSION.trim();
27
27
  authHeaders['NR-CSEC-COLLECTOR-TYPE'] = 'NODE';
@@ -29,7 +29,7 @@ function AuthHeaders() {
29
29
  authHeaders['NR-CSEC-APP-UUID'] = commonUtils.getUUID();
30
30
  authHeaders['NR-CSEC-JSON-VERSION'] = appInfo.jsonVersion;
31
31
  authHeaders['NR-CSEC-BUILD-NUMBER'] = appInfo.buildNumber;
32
- authHeaders['NR-ACCOUNT-ID'] = NRAgent.config.account_id;
32
+ authHeaders['NR-ACCOUNT-ID'] = NRAgent.config.account_id;
33
33
  authHeaders['NR-LICENSE-KEY'] = NRAgent ? NRAgent.config.license_key : '';
34
34
  authHeaders["NR-CSEC-IAST-DATA-TRANSFER-MODE"] = "PULL";
35
35
  } catch (error) {
@@ -49,6 +49,21 @@ AuthHeaders.prototype.constructor = AuthHeaders;
49
49
  function getInstance() {
50
50
  if (!authHeaders) {
51
51
  authHeaders = new AuthHeaders();
52
+ try {
53
+ for (const key in authHeaders) {
54
+ if (authHeaders.hasOwnProperty(key)) {
55
+ if (key != 'NR-LICENSE-KEY') {
56
+ logger.info(`Adding WS connection header: ${key} -> ${authHeaders[key]}`)
57
+ }
58
+ else {
59
+ logger.info(`Adding WS connection header: ${key} -> ******`)
60
+ }
61
+
62
+ }
63
+ }
64
+ } catch (error) {
65
+ logger.error("Error while getting authentication headers");
66
+ }
52
67
  }
53
68
  return authHeaders;
54
69
  }
@@ -26,13 +26,13 @@ let websocketHealth;
26
26
  * Utility to generate UUID
27
27
  * @returns UUID
28
28
  */
29
- function getUUID(){
29
+ function getUUID() {
30
30
  let uuid = process.env.applicationUUID || uuidV.v4();
31
31
  process.env.applicationUUID = uuid;
32
32
  return uuid;
33
33
  }
34
34
 
35
- function getKindIdPair (identifier, hostId) {
35
+ function getKindIdPair(identifier, hostId) {
36
36
  const obj = {};
37
37
  if (identifier.isContainer) {
38
38
  obj.first = 'CONTAINER';
@@ -54,7 +54,7 @@ function getKindIdPair (identifier, hostId) {
54
54
  * Utility to check worker thread support
55
55
  * @returns
56
56
  */
57
- function hasWorker () {
57
+ function hasWorker() {
58
58
  let workerFlag = false;
59
59
  try {
60
60
  require('worker_threads');
@@ -65,7 +65,7 @@ function hasWorker () {
65
65
  return workerFlag;
66
66
  }
67
67
 
68
- function runtimeSupportsFunctionGenerators () {
68
+ function runtimeSupportsFunctionGenerators() {
69
69
  try {
70
70
  // eslint-disable-next-line no-eval
71
71
  eval('"use strict"; (function* () {})');
@@ -76,7 +76,7 @@ function runtimeSupportsFunctionGenerators () {
76
76
  return process.csecRuntimeSupportsFunctionGenerators;
77
77
  }
78
78
 
79
- function runtimeSupportsAsyncFunctionGenerators () {
79
+ function runtimeSupportsAsyncFunctionGenerators() {
80
80
  try {
81
81
  // eslint-disable-next-line no-eval
82
82
  eval('"use strict"; (async function* () {})');
@@ -91,7 +91,7 @@ function runtimeSupportsAsyncFunctionGenerators () {
91
91
  * Utility to create directory and set permissions
92
92
  * @param {*} dir
93
93
  */
94
- function createPathIfNotExist (dir) {
94
+ function createPathIfNotExist(dir) {
95
95
  try {
96
96
  if (!fs.existsSync(dir)) {
97
97
  fs.mkdirSync(dir, 777, { recursive: true });
@@ -110,7 +110,7 @@ function createPathIfNotExist (dir) {
110
110
  * Utility to get OS
111
111
  * @returns
112
112
  */
113
- function getOS () {
113
+ function getOS() {
114
114
  if (process.platform === 'darwin') {
115
115
  return 'mac';
116
116
  }
@@ -124,7 +124,7 @@ function getOS () {
124
124
  /**
125
125
  * Utility to create directories
126
126
  */
127
- function createDirectories () {
127
+ function createDirectories() {
128
128
  createPathIfNotExist(`${CSEC_HOME}${SLASH}nr-security-home`);
129
129
  createPathIfNotExist(`${CSEC_HOME}${SLASH}nr-security-home${SLASH}logs`);
130
130
  createPathIfNotExist(`${CSEC_HOME}${SLASH}nr-security-home${SLASH}logs${SLASH}snapshots`);
@@ -140,7 +140,7 @@ function createDirectories () {
140
140
  /**
141
141
  * Utility to check init setup and disk space
142
142
  */
143
- async function initialSetup () {
143
+ async function initialSetup() {
144
144
  try {
145
145
  createDirectories();
146
146
  } catch (err) {
@@ -151,7 +151,7 @@ async function initialSetup () {
151
151
  /**
152
152
  * Utility function to check if the logfile is accessible or not.
153
153
  */
154
- function isLogAccessible (logFileName) {
154
+ function isLogAccessible(logFileName) {
155
155
  return new Promise((resolve) => {
156
156
  if (logFileName === njsAgentConstants.STATUS_LOG_FILE && require('./statusUtils').getBufferedHC().length === 0) {
157
157
  resolve('OK');
@@ -159,7 +159,11 @@ function isLogAccessible (logFileName) {
159
159
  fs.access(logFileName, fs.constants.F_OK | fs.constants.W_OK, (err) => {
160
160
  if (err) {
161
161
  resolve('Error');
162
- } else {
162
+ }
163
+ else if ((!logs.isLoggerOk || logger.level.levelStr == 'OFF') && logFileName != njsAgentConstants.STATUS_LOG_FILE) {
164
+ resolve('Error');
165
+ }
166
+ else {
163
167
  resolve('OK');
164
168
  }
165
169
  });
@@ -172,7 +176,7 @@ function isLogAccessible (logFileName) {
172
176
  * Utility function to get CPU Usage in percentage
173
177
  * @param {*} oldUsage
174
178
  */
175
- function getCpuUsage () {
179
+ function getCpuUsage() {
176
180
  const usageObj = process.cpuUsage();
177
181
  const userUsage = usageObj.user / (usageObj.user + usageObj.system);
178
182
  return userUsage;
@@ -183,7 +187,7 @@ function getCpuUsage () {
183
187
  * @param {*} bytes
184
188
  * @param {*} decimals
185
189
  */
186
- function formatBytesToMB (bytes, decimals = 2) {
190
+ function formatBytesToMB(bytes, decimals = 2) {
187
191
  return parseFloat((bytes / (1024 * 1024)).toFixed(decimals));
188
192
  }
189
193
 
@@ -191,34 +195,34 @@ function formatBytesToMB (bytes, decimals = 2) {
191
195
  * Utility to get diskSpace infor for the give path
192
196
  * @param {*} path
193
197
  */
194
- async function getDiskSpace (path) {
198
+ async function getDiskSpace(path) {
195
199
  try {
196
- const x = await checkDiskSpace(path).catch(() => {});
200
+ const x = await checkDiskSpace(path).catch(() => { });
197
201
  return x;
198
202
  } catch (error) {
199
- logger.error("Error in getting diskpace:",error)
203
+ logger.error("Error in getting diskpace:", error)
200
204
  }
201
205
  }
202
206
 
203
207
  /**
204
208
  * Utility to get HC stats
205
209
  */
206
- async function getHCStats () {
210
+ async function getHCStats() {
207
211
  const stats = {};
208
212
  try {
209
213
  const appInfo = require('./applicationinfo').getInstance();
210
214
  const rootDiskSpace = await getDiskSpace(path.parse(process.cwd()).root);
211
215
  if (appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications[0] && appInfo.serverInfo.deployedApplications[0].deployedPath) {
212
216
  const processDirDiskFreeSpace = await getDiskSpace(appInfo.serverInfo.deployedApplications[0].deployedPath);
213
- if(processDirDiskFreeSpace){
214
- stats.processDirDiskFreeSpaceMB = formatBytesToMB(processDirDiskFreeSpace.free);
217
+ if (processDirDiskFreeSpace) {
218
+ stats.processDirDiskFreeSpaceMB = formatBytesToMB(processDirDiskFreeSpace.free);
215
219
  }
216
220
  }
217
221
  const memoryObj = process.memoryUsage();
218
- if(rootDiskSpace){
222
+ if (rootDiskSpace) {
219
223
  stats.rootDiskFreeSpaceMB = formatBytesToMB(rootDiskSpace.free);
220
224
  }
221
-
225
+
222
226
  stats.processHeapUsageMB = formatBytesToMB(memoryObj.heapUsed);
223
227
  stats.processRssMB = formatBytesToMB(memoryObj.rss);
224
228
  stats.processMaxHeapMB = formatBytesToMB(memoryObj.heapTotal);
@@ -235,23 +239,23 @@ async function getHCStats () {
235
239
  /**
236
240
  * Utility to check Agent status
237
241
  */
238
- function isAgentActiveState () {
239
- if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true) && getWSHealthStatus()=='OK') {
242
+ function isAgentActiveState() {
243
+ if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true) && getWSHealthStatus() == 'OK') {
240
244
  return 'OK';
241
245
  } else {
242
246
  return 'Error';
243
247
  }
244
248
  }
245
249
 
246
- function iastRestClientStatus () {
250
+ function iastRestClientStatus() {
247
251
  return 'OK';
248
252
  }
249
253
  /**
250
254
  * Utiltity to remove older snapshots
251
255
  */
252
- function removeOlderSnapshots () {
256
+ function removeOlderSnapshots() {
253
257
  try {
254
- const basePath = njsAgentConstants.LOG_DIR + 'snapshots'+ SLASH;
258
+ const basePath = njsAgentConstants.LOG_DIR + 'snapshots' + SLASH;
255
259
  const directoryContent = fs.readdirSync(basePath);
256
260
  const files = directoryContent.filter((filename) => {
257
261
  return fs.statSync(`${basePath}${SLASH}${filename}`).isFile();
@@ -282,7 +286,7 @@ function removeOlderSnapshots () {
282
286
  /**
283
287
  * Utilty to remove older log files.
284
288
  */
285
- function removeOlderlogfiles () {
289
+ function removeOlderlogfiles() {
286
290
  try {
287
291
  const basePath = njsAgentConstants.LOG_DIR;
288
292
  const directoryContent = fs.readdirSync(basePath);
@@ -297,8 +301,11 @@ function removeOlderlogfiles () {
297
301
  const secondStat = fs.statSync(`${basePath}${SLASH}${b}`);
298
302
  return new Date(secondStat.mtime).getTime() - new Date(firstStat.mtime).getTime();
299
303
  });
300
-
301
- for (let i = 2; i < sorted.length; i++) {
304
+ let maxLogfile = process.env.NR_CSEC_DEBUG_LOGFILE_MAX_COUNT;
305
+ if(isNaN(maxLogfile)){
306
+ maxLogfile = 2;
307
+ }
308
+ for (let i = maxLogfile; i < sorted.length; i++) {
302
309
  const fileToDelete = basePath + sorted[i];
303
310
  try {
304
311
  fs.unlink(fileToDelete, (err) => {
@@ -320,7 +327,7 @@ function removeOlderlogfiles () {
320
327
  /**
321
328
  * utilty to roll over the log file. Not in use
322
329
  */
323
- function logRollOver () {
330
+ function logRollOver() {
324
331
  const logFileName = `${njsAgentConstants.LOG_DIR}node-security-collector.log`;
325
332
  try {
326
333
  const stats = fs.statSync(logFileName);
@@ -338,7 +345,7 @@ function logRollOver () {
338
345
  if (!fs.existsSync(lockFile)) {
339
346
  fs.writeFileSync(lockFile, pid.toString());
340
347
  }
341
- const fileData = fs.readFileSync(lockFile).toString();
348
+ const fileData = fs.readFileSync(lockFile).toString();
342
349
  if (pid.toString() === fileData) {
343
350
  try {
344
351
  const newLogFile = logFileName + '.' + Date.now();
@@ -360,9 +367,9 @@ function logRollOver () {
360
367
  * Utility to get validator service endpoint
361
368
  * @returns
362
369
  */
363
- function getValidatorServiceEndpointURL(){
364
- let validatorServiceEndpointURL = NRAgent ? NRAgent.config.security.validator_service_url: "wss://csec.nr-data.net";
365
- if(lodash.isEmpty(validatorServiceEndpointURL)){
370
+ function getValidatorServiceEndpointURL() {
371
+ let validatorServiceEndpointURL = NRAgent ? NRAgent.config.security.validator_service_url : "wss://csec.nr-data.net";
372
+ if (lodash.isEmpty(validatorServiceEndpointURL)) {
366
373
  validatorServiceEndpointURL = "wss://csec.nr-data.net";
367
374
  }
368
375
  return validatorServiceEndpointURL
@@ -372,7 +379,7 @@ function getValidatorServiceEndpointURL(){
372
379
  * Utility to get security mode
373
380
  * @returns
374
381
  */
375
- function getCSECmode(){
382
+ function getCSECmode() {
376
383
  const mode = NRAgent ? NRAgent.config.security.mode : 'IAST';
377
384
  return mode
378
385
  }
@@ -381,9 +388,9 @@ function getCSECmode(){
381
388
  * Utility to get path of CA Cert
382
389
  * @returns
383
390
  */
384
- function getPathOfCACert(){
391
+ function getPathOfCACert() {
385
392
  let relativePath = path.resolve(__dirname, '../../resources/cert.pem');
386
- if(process.platform == 'win32'){
393
+ if (process.platform == 'win32') {
387
394
  relativePath = path.resolve(__dirname, '..\\..\\resources\\cert.pem');
388
395
  }
389
396
  const certPath = NRAgent && NRAgent.config.security.ca_bundle_path ? NRAgent.config.security.ca_bundle_path : relativePath;
@@ -393,7 +400,7 @@ function getPathOfCACert(){
393
400
  * Setting ws health status
394
401
  * @param {*} status
395
402
  */
396
- function setWSHealthStatus(status){
403
+ function setWSHealthStatus(status) {
397
404
  websocketHealth = status;
398
405
  }
399
406
 
@@ -401,11 +408,11 @@ function setWSHealthStatus(status){
401
408
  * Returns value of websocketHealth
402
409
  * @returns websocketHealth
403
410
  */
404
- function getWSHealthStatus(){
405
- if(!websocketHealth){
411
+ function getWSHealthStatus() {
412
+ if (!websocketHealth) {
406
413
  return 'Error';
407
414
  }
408
- else{
415
+ else {
409
416
  return websocketHealth;
410
417
  }
411
418
  }
@@ -3,20 +3,53 @@
3
3
  * SPDX-License-Identifier: New Relic Pre-Release
4
4
  */
5
5
  const { Agent } = require('../../../agent');
6
- let completedRequestIds = new Set();
6
+ let pendingRequestIds = new Set();
7
+ let completedRequestsMap = new Map();
7
8
  const PolicyManager = require('../../../Policy');
8
9
  let batchSize;
9
- const batchthreshold = 300;
10
- function getCompletedRequestIds() {
11
- return [...completedRequestIds];
10
+ const batchthreshold = 160;
11
+ function getPendingRequestIds() {
12
+ return [...pendingRequestIds];
12
13
  }
13
14
 
14
- function addRequestId(id) {
15
- completedRequestIds.add(id);
15
+ function addPendingRequestId(id) {
16
+ pendingRequestIds.add(id);
16
17
  }
17
18
 
18
19
  function removeRequestId(id) {
19
- completedRequestIds.delete(id);
20
+ completedRequestsMap.delete(id);
21
+ }
22
+
23
+ function removePendingRequestId(id) {
24
+ pendingRequestIds.delete(id);
25
+ }
26
+
27
+ // for last leg
28
+ function completedRequestsMapInit(id) {
29
+ completedRequestsMap.set(id, []);
30
+ }
31
+
32
+ function addCompletedRequests(id, eventId) {
33
+ let data = completedRequestsMap.get(id);
34
+ data.push(eventId);
35
+ completedRequestsMap.set(id, data);
36
+ }
37
+
38
+ function getCompletedRequestsMap() {
39
+ return completedRequestsMap;
40
+ }
41
+
42
+ function clearCompletedRequestMap(){
43
+ completedRequestsMap.clear();
44
+ }
45
+
46
+ function clearPendingRequestIdSet(){
47
+ pendingRequestIds.clear();
48
+ }
49
+
50
+ function IASTCleanup(){
51
+ clearCompletedRequestMap();
52
+ clearPendingRequestIdSet();
20
53
  }
21
54
 
22
55
  function generateIASTDataRequest() {
@@ -27,24 +60,34 @@ function generateIASTDataRequest() {
27
60
  batchSize = policyInstance.data.vulnerabilityScan.iastScan.probing.batchSize;
28
61
  }
29
62
  }
30
- if (getCompletedRequestIds().length >= batchSize || batchSize < batchthreshold) {
63
+ if (getCompletedRequestsMap().size >= batchSize || batchSize < batchthreshold || getPendingRequestIds().length < batchSize) {
31
64
  batchSize = (2 * batchSize);
32
65
  }
33
66
  let object = {};
34
67
  object['jsonName'] = 'iast-data-request';
35
68
  object['applicationUUID'] = Agent.getAgent().applicationInfo.applicationUUID;
36
69
  object["batchSize"] = batchSize ? batchSize : 300;
37
- object['completedRequestIds'] = getCompletedRequestIds();
70
+ object['pendingRequestIds'] = getPendingRequestIds();
71
+ object['completedRequests'] = Object.fromEntries(getCompletedRequestsMap());
38
72
  return object;
39
-
40
73
  }
41
74
 
42
75
 
43
76
  module.exports = {
44
- getCompletedRequestIds,
45
- addRequestId,
46
- removeRequestId,
77
+ getPendingRequestIds,
78
+ addPendingRequestId,
79
+ removePendingRequestId,
47
80
  generateIASTDataRequest,
81
+
82
+ // for last leg
83
+ addCompletedRequests,
84
+ completedRequestsMapInit,
85
+ getCompletedRequestsMap,
86
+ removeRequestId,
87
+ IASTCleanup
88
+
89
+
90
+
48
91
  }
49
92
 
50
93
 
@@ -9,13 +9,12 @@ const stringify = require('fast-safe-stringify');
9
9
  const restClient = require('../../restclient');
10
10
  const { Agent } = require('../../../agent');
11
11
  const { FuzzFailEvent } = require('../../../FuzzFailEvent');
12
- const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH } = require('../../../sec-agent-constants');
12
+ const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP } = require('../../../sec-agent-constants');
13
13
  const LOCALHOST = 'localhost';
14
14
  const COLON_SLASH_SLASH = '://';
15
15
  const http = require('http');
16
16
  const https = require('https');
17
17
  const IASTUtil = require('./IASTUtils');
18
- const CSEC_SEP = ':IAST:';
19
18
 
20
19
  require('dns').setDefaultResultOrder('ipv4first')
21
20
 
@@ -46,10 +45,12 @@ function startIASTSchedular() {
46
45
  iastIntervalConst = setInterval(() => {
47
46
  let data = IASTUtil.generateIASTDataRequest();
48
47
  let currentTime = Date.now();
49
- let completedListSize = IASTUtil.getCompletedRequestIds().length;
48
+ let completedListSize = IASTUtil.getCompletedRequestsMap().size;
49
+ let pendingListSize = IASTUtil.getPendingRequestIds().length;
50
50
  let timeDiffInSeconds = ((currentTime - lastFuzzEventTime) / 1000);
51
51
  logger.trace("Time difference since last fuzz request:", timeDiffInSeconds);
52
52
  logger.trace("Completed requests so far:", completedListSize);
53
+ logger.trace("Pending requests so far:", pendingListSize);
53
54
 
54
55
  if (timeDiffInSeconds > 5 && additionalCoolDownTime == 0) {
55
56
  Agent.getAgent().client.dispatcher(data);
@@ -83,10 +84,7 @@ function handler(json) {
83
84
  try {
84
85
  fuzzRequest = JSON.parse(rawFuzzRequest);
85
86
  fuzzRequest['id'] = json.id;
86
- let completedList = IASTUtil.getCompletedRequestIds();
87
- if (completedList.includes(json.id)) {
88
- return;
89
- }
87
+ IASTUtil.addPendingRequestId(json.id);
90
88
  } catch (error) {
91
89
  logger.error('Parsing exeception in fuzz request: ', error);
92
90
  }
@@ -130,6 +128,10 @@ function handleFuzzRequest(fuzzDetails) {
130
128
  }
131
129
  }
132
130
 
131
+ config.headers['nr-csec-parent-id'] = fuzzRequest.id;
132
+
133
+ IASTUtil.completedRequestsMapInit(fuzzRequest.id);
134
+
133
135
  if(fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]){
134
136
  logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
135
137
  }
@@ -161,7 +163,7 @@ function handleFuzzRequest(fuzzDetails) {
161
163
  function handleFuzzResponse(response, fuzzDetails) {
162
164
  const { rawFuzzRequest, fuzzRequest } = fuzzDetails;
163
165
  if (response) {
164
- IASTUtil.addRequestId(fuzzRequest.id);
166
+ IASTUtil.removePendingRequestId(fuzzRequest.id);
165
167
  response.then(() => {
166
168
  logger.info('Fuzz success: ' + rawFuzzRequest);
167
169
  }).catch(() => {
@@ -178,7 +180,10 @@ function handleFuzzResponse(response, fuzzDetails) {
178
180
  */
179
181
  function parseAxiosHttpRequestToFuzz(requestObject) {
180
182
  let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
181
- let host = serverName + COLON + requestObject.serverPort
183
+ let host = serverName + COLON + requestObject.serverPort;
184
+ if(requestObject.headers && requestObject.headers['content-length']){
185
+ delete requestObject.headers['content-length'];
186
+ }
182
187
  return {
183
188
  url: requestObject.protocol + COLON_SLASH_SLASH + host + requestObject.url,
184
189
  method: requestObject.method,
@@ -9,16 +9,18 @@ const WebSocket = require('ws');
9
9
  const RingBuffer = require('ringbufferjs');
10
10
  const stringify = require('fast-safe-stringify');
11
11
  const { EventEmitter } = require('events');
12
-
12
+ const API = require('../../../../../nr-security-api');
13
+ const NRAgent = API.getNRAgent();
13
14
  const { Agent } = require('../../agent');
14
15
  const logs = require('../../logging');
15
16
  const hc = require('../../health-check');
16
17
  const fs = require('fs');
17
18
  const { promisify } = require('../../sec-util');
18
19
  const ResponseHandler = require('./response');
19
- const { EMPTY_APPLICATION_UUID, LOG_MESSAGES } = require('../../sec-agent-constants');
20
+ const { EMPTY_APPLICATION_UUID, LOG_MESSAGES, CSEC_SEP, NR_CSEC_FUZZ_REQUEST_ID, EXITEVENT, JSON_NAME, RASP } = require('../../sec-agent-constants');
20
21
  const statusUtils = require('../../statusUtils');
21
22
  const commonUtils = require('../../commonUtils');
23
+ const IASTUtil = require('../websocket/response/IASTUtils');
22
24
 
23
25
  const BUSY = 'busy';
24
26
  const IDLE = 'idle';
@@ -155,6 +157,7 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
155
157
  }
156
158
 
157
159
  initTimeOut = setTimeout(() => {
160
+ IASTUtil.IASTCleanup();
158
161
  logger.debug("Terminating ws instance and reconnecting");
159
162
  try {
160
163
  instance.terminate();
@@ -162,7 +165,7 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
162
165
  logger.debug("Error while terminating ws instance");
163
166
  }
164
167
  this.init();
165
- }, 15000)
168
+ }, (5 + (Math.floor(Math.random() * 11))) * 1000)
166
169
 
167
170
  }
168
171
 
@@ -200,6 +203,18 @@ SecWebSocket.prototype.dispatch = async function dispatch(event) {
200
203
  if (event.jsonName === APPLICATION_INFO_JSON_NAME) {
201
204
  APP_INFO = event;
202
205
  }
206
+ if (event.parentId && event.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
207
+ try {
208
+ let apiId = event.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID].split(CSEC_SEP)[0]
209
+ if (apiId == event.apiId) {
210
+ IASTUtil.addCompletedRequests(event.parentId, event.id);
211
+ }
212
+ } catch (error) {
213
+ logger.debug("Error while mapping completedRequests:", error);
214
+ }
215
+
216
+ }
217
+
203
218
  RegExp.prototype.toJSON = RegExp.prototype.toString;
204
219
  const eventStr = stringify(event);
205
220
  if (!this.instance ||
@@ -212,6 +227,20 @@ SecWebSocket.prototype.dispatch = async function dispatch(event) {
212
227
  try {
213
228
  await promisify(this.instance, this.instance.send)(eventStr, { mask: true }, () => {
214
229
  logger.debug(LOG_MESSAGES.EVENT_SENT + eventStr);
230
+ if (event.jsonName == JSON_NAME.EVENT) {
231
+
232
+ if (event.httpRequest && event.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
233
+ hc.getInstance().iastEventStats.sent++;
234
+ }
235
+ else {
236
+ hc.getInstance().raspEventStats.sent++;
237
+ }
238
+
239
+ }
240
+ if (event.jsonName == EXITEVENT) {
241
+ hc.getInstance().exitEventStats.sent++;
242
+ }
243
+
215
244
  });
216
245
  } catch (error) {
217
246
  logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error);
@@ -291,6 +320,13 @@ const handleDispatchFailure = (self, event, eventStr) => {
291
320
  // so feeding an old applicationinfo JSON would be incorrect
292
321
  if (eventStr && event.jsonName !== APPLICATION_INFO_JSON_NAME) {
293
322
  hc.getInstance().registerEventDrop();
323
+ if (event.httpRequest && event.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
324
+ hc.getInstance().iastEventStats.errorCount++;
325
+ }
326
+ else {
327
+ hc.getInstance().raspEventStats.errorCount++;
328
+ }
329
+
294
330
  }
295
331
  };
296
332
 
@@ -0,0 +1,27 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: New Relic Pre-Release
4
+ */
5
+
6
+ let eventStats = undefined;
7
+ function EventStats() {
8
+ this.processed = 0;
9
+ this.sent = 0;
10
+ this.rejected = 0;
11
+ this.errorCount = 0;
12
+ }
13
+ EventStats.prototype.constructor = EventStats;
14
+
15
+ /**
16
+ * Returns the current instanceof EventStats
17
+ *
18
+ * @returns {EventStats} instance
19
+ */
20
+ function getInstance() {
21
+ return new EventStats();
22
+ }
23
+
24
+
25
+ module.exports = {
26
+ getInstance
27
+ };
@@ -99,6 +99,7 @@ function SecEvent(fileName, funcName, lineNumber, argument, eid, applicationInfo
99
99
  }
100
100
  if (httpRequest && httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
101
101
  this.isIASTRequest = true;
102
+ this.parentId = httpRequest.headers['nr-csec-parent-id'];
102
103
  }
103
104
  else {
104
105
  this.isIASTRequest = false;
@@ -8,6 +8,7 @@
8
8
  const { Agent } = require('./agent');
9
9
  const { JSON_NAME } = require('./sec-agent-constants');
10
10
  const { BasicInfo } = require('./event');
11
+ const EventStats = require('./event-stats');
11
12
 
12
13
  const NODE_JS_LITERAL = 'Node.js:';
13
14
 
@@ -39,9 +40,10 @@ module.exports.getInstance = function () {
39
40
  *
40
41
  * @param {ApplicationInfo} info
41
42
  */
42
- function HC (info) {
43
+ function HC(info) {
43
44
  BasicInfo.call(this);
44
45
 
46
+
45
47
  this.applicationUUID = info.applicationUUID;
46
48
  this.eventType = 'sec_health_check_lc';
47
49
  if (NRAgent && NRAgent.config) {
@@ -58,7 +60,9 @@ function HC (info) {
58
60
  this.buildNumber = info.buildNumber;
59
61
  this.jsonName = JSON_NAME.HC;
60
62
  this.pid = info.pid;
61
-
63
+ this.iastEventStats = EventStats.getInstance();
64
+ this.raspEventStats = EventStats.getInstance();
65
+ this.exitEventStats = EventStats.getInstance();
62
66
 
63
67
 
64
68
  /**
@@ -130,6 +134,45 @@ function HC (info) {
130
134
  this.httpRequestCount = 0;
131
135
  };
132
136
 
137
+ /**
138
+ * resets the IAST EventStats.
139
+ */
140
+ this.resetIASTEventStats = function () {
141
+ this.iastEventStats.processed = 0;
142
+ this.iastEventStats.sent = 0;
143
+ this.iastEventStats.rejected = 0;
144
+ this.iastEventStats.errorCount = 0;
145
+ };
146
+
147
+ /**
148
+ * resets the RASP EventStats.
149
+ */
150
+ this.resetRASPEventStats = function () {
151
+ this.raspEventStats.processed = 0;
152
+ this.raspEventStats.sent = 0;
153
+ this.raspEventStats.rejected = 0;
154
+ this.raspEventStats.errorCount = 0;
155
+ };
156
+
157
+ /**
158
+ * resets the EXIT EventStats.
159
+ */
160
+ this.resetEXITEventStats = function () {
161
+ this.exitEventStats.processed = 0;
162
+ this.exitEventStats.sent = 0;
163
+ this.exitEventStats.rejected = 0;
164
+ this.exitEventStats.errorCount = 0;
165
+ };
166
+
167
+ /**
168
+ * resets the EventStats.
169
+ */
170
+ this.resetEventStats = function () {
171
+ this.resetIASTEventStats();
172
+ this.resetRASPEventStats();
173
+ this.resetEXITEventStats();
174
+ };
175
+
133
176
 
134
177
  /**
135
178
  * sets gerate hc timestamp.
@@ -24,7 +24,9 @@ const NRAgent = API.getNRAgent();
24
24
  const logFileName = `${LOG_DIR}node-security-collector.log`;
25
25
  const initLogFileName = `${LOG_DIR}node-security-collector-init.log`;
26
26
  let isLoggerOk = true;
27
-
27
+ const validLogLevels = ["trace", "debug", "info", "warn", "error", "mark", "fatal", "off", "all"];
28
+ const sep = require('path').sep;
29
+ const CSEC_ROOT = `${CSEC_HOME}${sep}nr-security-home${sep}`;
28
30
 
29
31
  function createLogPathIfNotExist(dir) {
30
32
  try {
@@ -45,14 +47,17 @@ function createLogPathIfNotExist(dir) {
45
47
  */
46
48
  function initialize(applicationUUID) {
47
49
  try {
48
- const logLevel = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.level : 'info';
50
+ let logLevel = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.level : 'info';
49
51
  const apmLogFilePath = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.filepath : '';
50
52
  let appenderType = 'file';
51
53
 
52
- if (apmLogFilePath === 'stdout') {
54
+ if (apmLogFilePath === 'stdout' || apmLogFilePath === 'stderr') {
53
55
  appenderType = 'stdout';
54
56
  }
55
57
 
58
+ if (logLevel && !validLogLevels.includes(logLevel.toLowerCase())) {
59
+ logLevel = 'info';
60
+ }
56
61
  const appenders = {};
57
62
  appenders.NR_CSEC = {
58
63
  type: appenderType,
@@ -67,6 +72,7 @@ function initialize(applicationUUID) {
67
72
 
68
73
 
69
74
  createLogPathIfNotExist(CSEC_HOME);
75
+ createLogPathIfNotExist(CSEC_ROOT);
70
76
  createLogPathIfNotExist(LOG_DIR);
71
77
 
72
78
 
@@ -117,7 +123,7 @@ function initialize(applicationUUID) {
117
123
  disableClustering: true
118
124
  });
119
125
  } catch (error) {
120
- console.error("Error while logging into file. Fallback to stdout.", error.message);
126
+ console.error(`Security agent is unable to write logs into file: ${logFileName}.`, error.message);
121
127
  isLoggerOk = false;
122
128
  }
123
129
  }
@@ -132,7 +138,7 @@ function setLevel(level = INFO) {
132
138
  logger.level = level;
133
139
  }
134
140
  if (!isLoggerOk) {
135
- logger.level = INFO;
141
+ logger.level = "off";
136
142
  }
137
143
  }
138
144
 
@@ -167,7 +173,7 @@ function getLogger(applicationUUID = process.env.applicationUUID) {
167
173
  } catch (error) {
168
174
  }
169
175
  initLogger.info("[STEP-2] => Generating unique identifier:", applicationUUID)
170
- if (NRAgent) {
176
+ if (NRAgent && NRAgent.config.logging.enabled) {
171
177
  setLevel(NRAgent.config.logging.level);
172
178
  }
173
179
 
@@ -196,5 +202,6 @@ module.exports = {
196
202
  setLevel,
197
203
  getLevel,
198
204
  getInitLogger,
199
- reCreateLogger
205
+ reCreateLogger,
206
+ isLoggerOk
200
207
  };
@@ -12,7 +12,7 @@ const API = require('../../../nr-security-api');
12
12
  const NRAgent = API.getNRAgent();
13
13
  const sep = require('path').sep;
14
14
 
15
- let CSEC_HOME = NRAgent && NRAgent.config.newrelic_home ? NRAgent.config.newrelic_home : require('path').join(process.cwd());
15
+ 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());
16
16
  const CSEC_GROUP_NAME = NRAgent ? NRAgent.config.security.mode : 'IAST';
17
17
  CSEC_HOME = path.resolve(CSEC_HOME);
18
18
  const agentStartTime = new Date().toString();
@@ -49,9 +49,9 @@ module.exports = {
49
49
  COLLECTOR_CONFIG: 100,
50
50
  BLOCKING: 101,
51
51
  POLICY_ERROR: 102,
52
- OBEY_WS_RECONNECT:12,
53
- ENTER_IAST_COOLDOWN:13,
54
- IAST_RECORD_DELETE_CONFIRMATION:14
52
+ OBEY_WS_RECONNECT: 12,
53
+ ENTER_IAST_COOLDOWN: 13,
54
+ IAST_RECORD_DELETE_CONFIRMATION: 14
55
55
 
56
56
  },
57
57
  JSON_NAME: {
@@ -105,6 +105,9 @@ module.exports = {
105
105
  DOUBLE_DOLLAR: '$$',
106
106
  LOADING_MODULE: 'Loading module ',
107
107
  EXITEVENT: 'exit-event',
108
+ CSEC_SEP: ':IAST:',
109
+ RASP: 'RASP',
110
+ IAST: 'IAST',
108
111
 
109
112
  LOG_MESSAGES: {
110
113
  LOADED_CSEC_ENVS_MSG: '[STEP-1][COMPLETE][env] Environment Information Gathering Done.',
@@ -9,9 +9,10 @@ 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 } = require('./sec-agent-constants');
12
+ const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP } = require('./sec-agent-constants');
13
13
  const API = require('../../../nr-security-api');
14
14
  const NRAgent = API.getNRAgent();
15
+ const HC = require('./health-check');
15
16
 
16
17
  const statusUtils = require('./statusUtils');
17
18
 
@@ -66,6 +67,12 @@ function generateSecEvent(securityMetadata) {
66
67
  const traceObject = securityMetadata.traceObject;
67
68
  const request = securityMetadata.request;
68
69
 
70
+ if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
71
+ HC.getInstance().iastEventStats.processed++;
72
+ }
73
+ else{
74
+ HC.getInstance().raspEventStats.processed++;
75
+ }
69
76
  try {
70
77
  let stakTrace = traceObject.stacktrace;
71
78
  let uri = EMPTY_STR;
@@ -95,6 +102,13 @@ function generateSecEvent(securityMetadata) {
95
102
  } catch (e) {
96
103
  statusUtils.addErrortoBuffer(e);
97
104
  logger.error("Error in generating event:", e);
105
+
106
+ if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
107
+ HC.getInstance().iastEventStats.errorCount++;
108
+ }
109
+ else{
110
+ HC.getInstance().raspEventStats.errorCount++;
111
+ }
98
112
  }
99
113
 
100
114
  }
@@ -122,10 +136,12 @@ function generateExitEvent(secEvent) {
122
136
  exitEvent.executionId = secEvent.id;
123
137
  exitEvent.caseType = secEvent.caseType;
124
138
  exitEvent.k2RequestIdentifier = secEvent.httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID];
139
+ HC.getInstance().exitEventStats.processed++;
125
140
  sendEvent(exitEvent);
126
141
  }
127
142
  } catch (error) {
128
143
  logger.error('Error in generating exit event', error);
144
+ HC.getInstance().exitEventStats.errorCount++;
129
145
  }
130
146
  }
131
147
  }
@@ -23,7 +23,7 @@ const {
23
23
  }
24
24
  } = require('./connections/websocket');
25
25
  const { HC_INTERVAL_MS } = require('../../resources/config');
26
- const { LOG_DIR } = require('./sec-agent-constants');
26
+ const { LOG_DIR, EXITEVENT, JSON_NAME } = require('./sec-agent-constants');
27
27
  const statusUtils = require('./statusUtils');
28
28
  const njsAgentConstants = require('./sec-agent-constants');
29
29
 
@@ -100,6 +100,7 @@ const sendHC = async function () {
100
100
  json = JSON.parse(json);
101
101
  json.policyVersion = applicationInfo.policyVersion;
102
102
  json.entityGuid = applicationInfo.entityGuid;
103
+
103
104
  try {
104
105
  json.stats = await commonUtils.getHCStats();
105
106
  } catch (error) {
@@ -114,10 +115,17 @@ const sendHC = async function () {
114
115
  json.serviceStatus.agentActiveStat = commonUtils.isAgentActiveState();
115
116
  json.serviceStatus.iastRestClient = commonUtils.iastRestClientStatus();
116
117
 
118
+ json.exitEventSentCount = json.exitEventStats.sent;
119
+ json.eventRejectionCount = json.raspEventStats.rejected + json.iastEventStats.rejected;
120
+ json.eventProcessingErrorCount = json.raspEventStats.errorCount + json.iastEventStats.errorCount;
121
+ json.eventSendRejectionCount = json.eventRejectionCount;
122
+ json.eventSendErrorCount = json.eventProcessingErrorCount;
123
+
117
124
  hc.getInstance().resetDropCount();
118
125
  hc.getInstance().resetProcessedCount();
119
126
  hc.getInstance().resetEventSentCount();
120
127
  hc.getInstance().resetHttpRequestCount();
128
+ hc.getInstance().resetEventStats();
121
129
 
122
130
  logger.info('Health Check Status:', JSON.stringify(json));
123
131
  statusUtils.addHCtoBuffer(json);
@@ -133,13 +141,13 @@ const sendHC = async function () {
133
141
  */
134
142
  function getDispatcherAndSendEvent(eventJson) {
135
143
  const eventType = eventJson.jsonName;
136
- if (eventType === 'Event') {
144
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
137
145
  hc.getInstance().registerEventProcessed();
138
146
  }
139
147
 
140
148
  dispatcher(eventJson);
141
149
 
142
- if (eventType === 'Event') {
150
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
143
151
  hc.getInstance().registerEventSent();
144
152
  }
145
153
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
- "jsonVersion": "1.0.1",
6
+ "jsonVersion": "1.1.0",
7
7
  "contributors": [
8
8
  {
9
9
  "name": "Sumit Suthar",
@@ -35,8 +35,8 @@
35
35
  "url": "https://github.com/newrelic/csec-node-agent.git"
36
36
  },
37
37
  "dependencies": {
38
- "@aws-sdk/client-lambda": "^3.363.0",
39
- "axios": "0.21.4",
38
+ "@aws-sdk/client-lambda": "^3.405.0",
39
+ "axios": "1.6.0",
40
40
  "check-disk-space": "3.3.1",
41
41
  "content-type": "^1.0.5",
42
42
  "fast-safe-stringify": "^2.1.1",
@@ -55,7 +55,7 @@
55
55
  "sync-request": "^6.1.0",
56
56
  "unescape": "^1.0.1",
57
57
  "unescape-js": "^1.1.4",
58
- "uuid": "^9.0.0",
58
+ "uuid": "^9.0.1",
59
59
  "ws": "^7.5.9"
60
60
  },
61
61
  "devDependencies": {