@newrelic/security-agent 0.4.0 → 0.6.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 (32) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -0
  3. package/THIRD_PARTY_NOTICES.md +17 -18
  4. package/lib/instrumentation-security/core/sec-utils.js +1 -0
  5. package/lib/instrumentation-security/hooks/@nestjs/nr-nestjs-core.js +63 -0
  6. package/lib/instrumentation-security/hooks/express/nr-express.js +2 -0
  7. package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +1 -0
  8. package/lib/instrumentation-security/hooks/fs/nr-fs.js +18 -13
  9. package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +2 -1
  10. package/lib/instrumentation-security/hooks/http/nr-http.js +31 -3
  11. package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +3 -1
  12. package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +1 -0
  13. package/lib/instrumentation-security/hooks/mongodb/common.js +2 -0
  14. package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +1 -1
  15. package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +5 -2
  16. package/lib/instrumentation-security/hooks/native/nr-childProcess.js +3 -0
  17. package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +1 -0
  18. package/lib/instrumentation-security/hooks/restify/nr-restify.js +3 -1
  19. package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +4 -2
  20. package/lib/instrumentation-security/index.js +9 -0
  21. package/lib/nr-security-agent/index.js +9 -4
  22. package/lib/nr-security-agent/lib/core/Auth-headers.js +7 -0
  23. package/lib/nr-security-agent/lib/core/LogMessage.js +39 -14
  24. package/lib/nr-security-agent/lib/core/applicationinfo.js +1 -1
  25. package/lib/nr-security-agent/lib/core/commonUtils.js +28 -4
  26. package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +21 -6
  27. package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +5 -2
  28. package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +21 -2
  29. package/lib/nr-security-agent/lib/core/sec-agent-constants.js +3 -0
  30. package/lib/nr-security-agent/lib/core/sec-util.js +14 -6
  31. package/lib/nr-security-agent/lib/core/websocket-client.js +9 -8
  32. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ### v0.6.0 (2024-01-03)
2
+ #### Features
3
+ * Added ws headers NR-CSEC-ENTITY-GUID and NR-CSEC-ENTITY-NAME
4
+ * Updated jsonVersion to 1.1.1 in security events
5
+ * Support to send important logs/errors to security engine
6
+ #### Bug fixes
7
+ * Added missing protocol in http request object
8
+ * Fix for honouring probing interval from policy
9
+ #### Miscellaneous chores
10
+ * Added nestjs test cases
11
+ * Additional logging for instrumented modules and methods
12
+ * Upgraded axios to v1.6.3
13
+ * Removed pinned version for axios and check-disk-space
14
+ * Update in lockfileVersion of package-lock.json
15
+ * Updated @babel/traverse, protobufjs, fast-xml-parser and @aws-sdk/credential-providers
16
+ * Updated Readme.md
17
+
18
+
19
+ ### v0.5.0 (2023-11-17)
20
+ #### Features
21
+ * IAST support for NestJS framework
22
+ #### Bug Fixes
23
+ * Fixed misspelled constant in fs.open() hook
24
+ * Minor fix in applicationInfo for pod properties
25
+ #### Miscellaneous chores
26
+ * Upgraded ws to v8.14.2 and updated initialization of websocket for v8.x.
27
+ * Upgraded check-disk-space to v3.4.0
28
+ * Upgraded @aws-sdk/client-lambda to v3.436.0
29
+
1
30
  ### v0.4.0 (2023-10-27)
2
31
  #### Features
3
32
  * Added event stats for RASP, IAST and exit events in healthcheck.
package/README.md CHANGED
@@ -62,6 +62,7 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
62
62
  - [fastify](https://www.npmjs.com/package/fastify)(2.x, 3.x and 4.x)
63
63
  - [restify](https://www.npmjs.com/package/restify)(8.x, 9.x, 10.x and 11.x)
64
64
  - [director](https://www.npmjs.com/package/director)(1.2.x)
65
+ - [@nestjs/core](https://www.npmjs.com/package/@nestjs/core)(8.x and above)
65
66
  - [ldapjs](https://www.npmjs.com/package/ldapjs)(2.x and above)
66
67
  - [ldapts](https://www.npmjs.com/package/ldapts)(2.x and above)
67
68
  - [xpath](https://www.npmjs.com/package/xpath)(0.0.20 and above)
@@ -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.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):
78
+ This product includes source derived from [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3) ([v3.436.0](https://github.com/aws/aws-sdk-js-v3/tree/v3.436.0)), distributed under the [Apache-2.0 License](https://github.com/aws/aws-sdk-js-v3/blob/v3.436.0/LICENSE):
79
79
 
80
80
  ```
81
81
  Apache License
@@ -284,7 +284,7 @@ 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) ([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):
287
+ This product includes source derived from [axios](https://github.com/axios/axios) ([v1.6.3](https://github.com/axios/axios/tree/v1.6.3)), distributed under the [MIT License](https://github.com/axios/axios/blob/v1.6.3/LICENSE):
288
288
 
289
289
  ```
290
290
  # Copyright (c) 2014-present Matt Zabriskie & Collaborators
@@ -299,7 +299,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
299
299
 
300
300
  ### check-disk-space
301
301
 
302
- This product includes source derived from [check-disk-space](https://github.com/Alex-D/check-disk-space) ([v3.3.1](https://github.com/Alex-D/check-disk-space/tree/v3.3.1)), distributed under the [MIT License](https://github.com/Alex-D/check-disk-space/blob/v3.3.1/LICENSE):
302
+ This product includes source derived from [check-disk-space](https://github.com/Alex-D/check-disk-space) ([v3.4.0](https://github.com/Alex-D/check-disk-space/tree/v3.4.0)), distributed under the [MIT License](https://github.com/Alex-D/check-disk-space/blob/v3.4.0/LICENSE):
303
303
 
304
304
  ```
305
305
  MIT License
@@ -795,30 +795,29 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
795
795
 
796
796
  ### ws
797
797
 
798
- This product includes source derived from [ws](https://github.com/websockets/ws) ([v7.5.9](https://github.com/websockets/ws/tree/v7.5.9)), distributed under the [MIT License](https://github.com/websockets/ws/blob/v7.5.9/LICENSE):
798
+ This product includes source derived from [ws](https://github.com/websockets/ws) ([v8.14.2](https://github.com/websockets/ws/tree/v8.14.2)), distributed under the [MIT License](https://github.com/websockets/ws/blob/v8.14.2/LICENSE):
799
799
 
800
800
  ```
801
- The MIT License (MIT)
802
-
803
801
  Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
802
+ Copyright (c) 2013 Arnout Kazemier and contributors
803
+ Copyright (c) 2016 Luigi Pinca and contributors
804
804
 
805
- Permission is hereby granted, free of charge, to any person obtaining a copy
806
- of this software and associated documentation files (the "Software"), to deal
807
- in the Software without restriction, including without limitation the rights
808
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
809
- copies of the Software, and to permit persons to whom the Software is
810
- furnished to do so, subject to the following conditions:
805
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
806
+ this software and associated documentation files (the "Software"), to deal in
807
+ the Software without restriction, including without limitation the rights to
808
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
809
+ the Software, and to permit persons to whom the Software is furnished to do so,
810
+ subject to the following conditions:
811
811
 
812
812
  The above copyright notice and this permission notice shall be included in all
813
813
  copies or substantial portions of the Software.
814
814
 
815
815
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
816
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
817
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
818
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
819
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
820
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
821
- SOFTWARE.
816
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
817
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
818
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
819
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
820
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
822
821
 
823
822
  ```
824
823
 
@@ -172,6 +172,7 @@ function addRequestData(shim, request) {
172
172
  const data = Object.assign({});
173
173
  const segment = shim.getActiveSegment();
174
174
  if (segment && segment.transaction) {
175
+ data.protocol = (request.connection && request.connection.encrypted) ? 'https' : 'http';
175
176
  data.body = null;
176
177
  data.headers = request.headers;
177
178
  data.url = request.url;
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Copyright 2023 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: New Relic Pre-Release
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const secUtils = require('../../core/sec-utils');
9
+ const { ATTHERATE, DOUBLE_DOLLAR, EMPTY_STR } = require('../../core/constants');
10
+ const routeManager = require('../../core/route-manager');
11
+ const API = require("../../../nr-security-api");
12
+ const logger = API.getLogger();
13
+ const semver = require('semver')
14
+
15
+ const RequestMethods = {
16
+ "0": "GET",
17
+ "1": "POST",
18
+ "2": "PUT",
19
+ "3": "DELETE",
20
+ "4": "PATCH",
21
+ "5": "ALL",
22
+ "6": "OPTIONS",
23
+ "7": "HEAD",
24
+ "8": "SEARCH",
25
+ }
26
+
27
+ module.exports = function initialize(shim) {
28
+ const nestJsVersion = shim.require('./package.json').version
29
+ logger.info("Instrumenting nestjs", nestJsVersion)
30
+ if (semver.lt(nestJsVersion, '8.0.0')) {
31
+ logger.debug(
32
+ `Not instrumenting Nest.js version ${nestJsVersion}; minimum instrumentable version is 8.0.0`
33
+ )
34
+ return
35
+ }
36
+
37
+ const routerExplorer = shim.require("./router/router-explorer").RouterExplorer;
38
+
39
+ shim.wrap(routerExplorer.prototype, 'applyCallbackToRouter', function wrapApplyCallbackToRouter(shim, fn) {
40
+ if (!shim.isFunction(fn)) {
41
+ return fn
42
+ }
43
+ logger.debug(`Instrumenting routerExplorer.prototype.applyCallbackToRouter`)
44
+ return function wrappedApplyCallbackToRouter() {
45
+ try {
46
+ const stakTrace = secUtils.traceElementForRoute();
47
+ const splittedStack = stakTrace[0].split(DOUBLE_DOLLAR);
48
+ const verb = RequestMethods[arguments[1].requestMethod];
49
+ const path = (arguments[4].ctrlPath) + (arguments[1].path[0] == '/' ? EMPTY_STR : arguments[1].path[0]);
50
+ const key = verb + ATTHERATE + path;
51
+ routeManager.setRoute(key, splittedStack[0]);
52
+ } catch (error) {
53
+ }
54
+ return fn.apply(this, arguments);
55
+ }
56
+ })
57
+
58
+ }
59
+
60
+
61
+
62
+
63
+
@@ -42,6 +42,7 @@ function wrapExpress4(shim, express) {
42
42
  if (!shim.isFunction(fn)) {
43
43
  return fn
44
44
  }
45
+ logger.debug('Instrumenting express.Router.route');
45
46
  return function wrappedRoute() {
46
47
  try {
47
48
  const stakTrace = secUtils.traceElementForRoute();
@@ -62,6 +63,7 @@ function wrapExpress4(shim, express) {
62
63
  if (!shim.isFunction(fn)) {
63
64
  return fn
64
65
  }
66
+ logger.debug('Instrumenting express.Router.process_params')
65
67
  return function wrappedParam() {
66
68
  extractParams(shim, arguments[2]);
67
69
  const route = fn.apply(this, arguments)
@@ -40,6 +40,7 @@ const setupRouteHandler = (shim, fastify, moduleName) => {
40
40
  if (!shim.isFunction(fn)) {
41
41
  return fn
42
42
  }
43
+ logger.debug(`Instrumenting ${moduleName}.${fun}`);
43
44
  return function wrappedRoute() {
44
45
  try {
45
46
  const stakTrace = secUtils.traceElementForRoute();
@@ -94,8 +94,8 @@ const promiseFunctionProbableToFI = [
94
94
  */
95
95
  function initialize(shim, mod, moduleName) {
96
96
 
97
-
98
- logger.info("Instrumenting FS module");
97
+
98
+ logger.info(`Instrumenting ${moduleName}`);
99
99
  requireHook.initialize(shim)
100
100
  const binding = process.binding("fs");
101
101
  openHook(shim, binding, moduleName);
@@ -116,6 +116,7 @@ function initialize(shim, mod, moduleName) {
116
116
  */
117
117
  function openHook(shim, mod, moduleName) {
118
118
  shim.wrap(mod, "open", function makeOpenWrapper(shim, fn) {
119
+ logger.debug(`Instrumenting ${moduleName}.open`);
119
120
  return function openWrapper() {
120
121
  const parameters = Array.prototype.slice.apply(arguments);
121
122
  const interceptedArgs = [arguments[0]];
@@ -128,7 +129,7 @@ function openHook(shim, mod, moduleName) {
128
129
  parameters[0] = arguments[0];
129
130
  }
130
131
  }
131
- if (request && typeof parameters[0] === STRING && !lodash.isEmpty(parameters[0]) ) {
132
+ if (request && typeof parameters[0] === STRING && !lodash.isEmpty(parameters[0])) {
132
133
  const policy = API.getPolicy();
133
134
  const dynamicScanningFlag = policy.data ? policy.data.vulnerabilityScan.iastScan.enabled : false;
134
135
  const url = request.url;
@@ -136,7 +137,7 @@ function openHook(shim, mod, moduleName) {
136
137
  const trimedURL = url.split(QUESTIONMARK)[0];
137
138
  if (!trimedURL.includes(path.basename(parameters[0])) || decodedURL.includes(DOTDOTSLASH) || dynamicScanningFlag) {
138
139
  try {
139
- if (parameters[0].startsWith(DOTDOTSLADH)) {
140
+ if (parameters[0].startsWith(DOTDOTSLASH)) {
140
141
  parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0];
141
142
  } else if (parameters[0].startsWith(SLASHDOTDOT)) {
142
143
  parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0];
@@ -151,14 +152,14 @@ function openHook(shim, mod, moduleName) {
151
152
  this.secEvent = API.generateSecEvent(secMetadata);
152
153
  API.sendEvent(this.secEvent);
153
154
  const callbackFlag = isCallback(arguments);
154
- if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
155
+ if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
155
156
  callbackHook(shim, arguments[3], 'oncomplete', this.secEvent);
156
157
  }
157
158
  }
158
159
 
159
160
  }
160
161
  const result = fn.apply(this, arguments);
161
- if(result>0 && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
162
+ if (result > 0 && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
162
163
  //here generate exit event.
163
164
  API.generateExitEvent(this.secEvent);
164
165
  delete this.secEvent;
@@ -177,18 +178,19 @@ function openHook(shim, mod, moduleName) {
177
178
  function probableToFAHooks(shim, mod, moduleName) {
178
179
  functionsProbableToFA.forEach(function (fun) {
179
180
  shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
181
+ logger.debug(`Instrumenting ${moduleName}.${fun}`);
180
182
  return function FAWrapper() {
181
183
  const interceptedArgs = [arguments[0]];
182
184
  shim.interceptedArgs = interceptedArgs;
183
185
  const request = requestManager.getRequest(shim);
184
- if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
186
+ if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
185
187
  const traceObject = secUtils.getTraceObject(shim);
186
188
  const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
187
189
  this.secEvent = API.generateSecEvent(secMetadata);
188
190
  API.sendEvent(this.secEvent);
189
191
  const callbackFlag = isCallback(arguments);
190
- if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
191
- callbackHook(shim, arguments, arguments.length-1, this.secEvent);
192
+ if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
193
+ callbackHook(shim, arguments, arguments.length - 1, this.secEvent);
192
194
  }
193
195
  }
194
196
  return fn.apply(this, arguments);
@@ -206,11 +208,12 @@ function probableToFAHooks(shim, mod, moduleName) {
206
208
  function probableToFIHooks(shim, mod, moduleName) {
207
209
  functionProbableToFI.forEach(function (fun) {
208
210
  shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
211
+ logger.debug(`Instrumenting ${moduleName}.${fun}`);
209
212
  return function FIWrapper() {
210
213
  const interceptedArgs = [arguments[0]];
211
214
  shim.interceptedArgs = interceptedArgs;
212
215
  const request = requestManager.getRequest(shim);
213
- if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
216
+ if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
214
217
  const traceObject = secUtils.getTraceObject(shim);
215
218
  if (fun === COPY_FILE || fun === RENAME) {
216
219
  const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
@@ -222,7 +225,7 @@ function probableToFIHooks(shim, mod, moduleName) {
222
225
  API.sendEvent(this.secEvent);
223
226
  const callbackFlag = isCallback(arguments);
224
227
  if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
225
- callbackHook(shim, arguments, arguments.length-1, this.secEvent);
228
+ callbackHook(shim, arguments, arguments.length - 1, this.secEvent);
226
229
  }
227
230
  }
228
231
  return fn.apply(this, arguments);
@@ -239,12 +242,13 @@ function probableToFIHooks(shim, mod, moduleName) {
239
242
  */
240
243
  function probablePromiseToFAHooks(shim, mod, moduleName) {
241
244
  promiseFunctionsProbableToFA.forEach(function (fun) {
245
+ logger.debug(`Instrumenting Promise ${moduleName}.${fun}`);
242
246
  shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
243
247
  return function FAWrapper() {
244
248
  const interceptedArgs = [arguments[0]];
245
249
  shim.interceptedArgs = interceptedArgs;
246
250
  const request = requestManager.getRequest(shim);
247
- if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
251
+ if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
248
252
  const traceObject = secUtils.getTraceObject(shim);
249
253
  const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
250
254
  this.secEvent = API.generateSecEvent(secMetadata);
@@ -264,12 +268,13 @@ function probablePromiseToFAHooks(shim, mod, moduleName) {
264
268
  */
265
269
  function probablePromiseToFIHooks(shim, mod, moduleName) {
266
270
  promiseFunctionProbableToFI.forEach(function (fun) {
271
+ logger.debug(`Instrumenting Promise ${moduleName}.${fun}`);
267
272
  shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
268
273
  return function FIWrapper() {
269
274
  const interceptedArgs = [arguments[0]];
270
275
  shim.interceptedArgs = interceptedArgs;
271
276
  const request = requestManager.getRequest(shim);
272
- if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
277
+ if (request && typeof arguments[0] === STRING && !lodash.isEmpty(arguments[0])) {
273
278
  const traceObject = secUtils.getTraceObject(shim);
274
279
  if (fun === COPY_FILE || fun === RENAME) {
275
280
  const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
@@ -25,7 +25,7 @@ module.exports = function initialize(shim, hapi, moduleName) {
25
25
  return
26
26
  }
27
27
 
28
- logger.info("Instrumenting Hapi")
28
+ logger.info(`Instrumenting ${moduleName}`);
29
29
  // 'Server' and 'server' both point to the same export,
30
30
  // but we can't make any assumption about which will be used.
31
31
  // Since we wrap the prototype, the second wrap should exit early.
@@ -49,6 +49,7 @@ function serverPostConstructor(shim) {
49
49
  */
50
50
  function wrapProtoRoute(shim, proto) {
51
51
  shim.wrap(proto, 'route', function wrapRoute(shim, original) {
52
+ logger.debug(`Instrumenting ${proto}.route`);
52
53
  return function wrappedRoute() {
53
54
  const args = shim.argsToArray.apply(shim, arguments)
54
55
  if (!shim.isObject(args[0])) {
@@ -46,7 +46,7 @@ const CSEC_HOME_TMP = `${CSEC_HOME}/nr-security-home/tmp/language-agent/${proces
46
46
  * @param {*} moduleName
47
47
  */
48
48
  function initialize(shim, mod, moduleName) {
49
- logger.info('Instrumenting HTTP module');
49
+ logger.info(`Instrumenting ${moduleName}`);
50
50
  emitHook(shim, mod, moduleName);
51
51
  createServerHook(shim, mod, moduleName);
52
52
  outboundHook(shim, mod, 'request', moduleName);
@@ -87,7 +87,11 @@ function setupRequestMapCleanup() {
87
87
  * @param {*} moduleName
88
88
  */
89
89
  function outboundHook(shim, mod, method, moduleName) {
90
+ logger.debug(`Instrumenting ${moduleName}.${method}`);
90
91
  shim.wrap(mod, method, function makeRequestWrapper(shim, fn) {
92
+ if (!shim.isFunction(fn)) {
93
+ return fn
94
+ }
91
95
  return function requestWrapper() {
92
96
  const request = requestManager.getRequest(shim);
93
97
  const data = {};
@@ -206,6 +210,7 @@ function addRequestData(shim, request) {
206
210
  const data = Object.assign({});
207
211
  const segment = shim.getActiveSegment();
208
212
  if (segment && segment.transaction) {
213
+ data.protocol = (request.connection && request.connection.encrypted) ? 'https' : 'http';
209
214
  data.body = null;
210
215
  data.headers = request.headers;
211
216
  data.url = request.url;
@@ -255,6 +260,10 @@ function addRequestData(shim, request) {
255
260
  */
256
261
  function emitHook(shim, mod, moduleName) {
257
262
  shim.wrap(mod && mod.Server && mod.Server.prototype, 'emit', function makeEmitWrapper(shim, fn) {
263
+ logger.debug(`Instrumenting ${moduleName}.Server.prototype.emit`);
264
+ if (!shim.isFunction(fn)) {
265
+ return fn
266
+ }
258
267
  return function emitWrapper() {
259
268
  const req = arguments[1];
260
269
  const resp = arguments[2];
@@ -263,8 +272,11 @@ function emitHook(shim, mod, moduleName) {
263
272
  if (NRAgent && NRAgent.config.security.detection.rxss.enabled) {
264
273
  responseHook(resp, req, shim);
265
274
  }
266
-
275
+
267
276
  shim.wrap(req, 'on', function makeOnWrapper(shim, fn) {
277
+ if (!shim.isFunction(fn)) {
278
+ return fn
279
+ }
268
280
  return function OnWrapper() {
269
281
  if (!requestManager.getRequest(shim)) {
270
282
  addRequestData(shim, req);
@@ -292,10 +304,17 @@ function emitHook(shim, mod, moduleName) {
292
304
  * @param {*} shim
293
305
  * @param {*} mod
294
306
  */
295
- function createServerHook(shim, mod) {
307
+ function createServerHook(shim, mod, moduleName) {
296
308
  shim.wrap(mod, 'createServer', function createServerWrapper(shim, fn) {
309
+ logger.debug(`Instrumenting ${moduleName}.createServer`);
310
+ if (!shim.isFunction(fn)) {
311
+ return fn
312
+ }
297
313
  return function wrappedCreateServer() {
298
314
  shim.wrap(arguments, '0', function appWrapper(shim, fn) {
315
+ if (!shim.isFunction(fn)) {
316
+ return fn
317
+ }
299
318
  return function wrappedApp() {
300
319
  let req = arguments[0];
301
320
  if (!requestManager.getRequest(shim)) {
@@ -316,6 +335,9 @@ function createServerHook(shim, mod) {
316
335
  */
317
336
  function onDataHook(shim, mod) {
318
337
  shim.wrap(mod, '1', function makeOnDataWrapper(shim, fn) {
338
+ if (!shim.isFunction(fn)) {
339
+ return fn
340
+ }
319
341
  return function onDataWrapper() {
320
342
  const chunk = arguments[0];
321
343
  const chunkType = typeof chunk;
@@ -347,6 +369,9 @@ function responseHook(resp, req, shim) {
347
369
  resp.res.body = EMPTY_STRING;
348
370
 
349
371
  shim.wrap(resp, 'write', function makeWriteWrapper(shim, fn) {
372
+ if (!shim.isFunction(fn)) {
373
+ return fn
374
+ }
350
375
  return function wrappedWrite() {
351
376
  const response = this;
352
377
  responseBodyCompute(response, arguments);
@@ -355,6 +380,9 @@ function responseHook(resp, req, shim) {
355
380
  })
356
381
  // wrapper for response.end()
357
382
  shim.wrap(resp, 'end', function makeEndWrapper(shim, fn) {
383
+ if (!shim.isFunction(fn)) {
384
+ return fn
385
+ }
358
386
  return function wrappedEnd() {
359
387
  const response = this;
360
388
  responseBodyCompute(response, arguments);
@@ -20,7 +20,7 @@ const logger = API.getLogger();
20
20
  * @param {*} moduleName
21
21
  */
22
22
  function initialize(shim, mod, moduleName) {
23
- logger.info('Instrumenting @koa/router');
23
+ logger.info(`Instrumenting ${moduleName}`);
24
24
  const VerbMethods = ['all', 'delete', 'get', 'head', 'opts', 'post', 'put', 'patch'];
25
25
 
26
26
  // RouteMap hooks
@@ -29,6 +29,7 @@ function initialize(shim, mod, moduleName) {
29
29
  if (!shim.isFunction(fn)) {
30
30
  return fn
31
31
  }
32
+ logger.debug(`Instrumenting ${moduleName}.prototype.${fun}`);
32
33
  return function wrappedRoute() {
33
34
  try {
34
35
  const stakTrace = secUtils.traceElementForRoute();
@@ -59,6 +60,7 @@ function initialize(shim, mod, moduleName) {
59
60
  */
60
61
  function paramHook(shim, mod, moduleName) {
61
62
  shim.wrap(mod && mod.prototype, 'params', function wrapMatch(shim, fn) {
63
+ logger.debug(`Instrumenting ${moduleName}.prototype.params`);
62
64
  return function wrappedMatch() {
63
65
  const segment = shim.getActiveSegment();
64
66
  if (segment && segment.transaction) {
@@ -35,6 +35,7 @@ function initialize(shim, mod, moduleName){
35
35
  */
36
36
  function ldapHooks(shim, mod, methodName, moduleName){
37
37
  shim.wrap(mod, methodName, function makeWrapper(shim,fn){
38
+ logger.debug(`Instrumenting ${moduleName}.${methodName}`);
38
39
  return function wrapper(){
39
40
  const param = {};
40
41
  param.name = arguments[0];
@@ -55,6 +55,7 @@ function payloadType(obj) {
55
55
 
56
56
  function queryHook(shim, mod, method) {
57
57
  shim.wrap(mod, method, function makeQueryWrapper(shim, fn) {
58
+ logger.debug(`Instrumenting mongodb.${method}`);
58
59
  return function queryWrapper() {
59
60
  const request = requestManager.getRequest(shim);
60
61
  if (CURSOR_OPS.includes(method)) {
@@ -194,6 +195,7 @@ function callbackHook(shim, mod, fun, secEvent) {
194
195
 
195
196
  common.queryHookV2 = function queryHookV2(shim, mod, method) {
196
197
  shim.wrap(mod, method, function makeQueryWrapper(shim, fn) {
198
+ logger.debug(`Instrumenting mongodb.${method}`);
197
199
  return function queryWrapper() {
198
200
  const request = requestManager.getRequest(shim);
199
201
  if (CURSOR_OPS.includes(method)) {
@@ -32,7 +32,7 @@ module.exports = initialize;
32
32
  * @param {Shim} shim
33
33
  */
34
34
  function initialize(shim, mongodb, moduleName) {
35
- logger.info("Instrumenting Mongodb");
35
+ logger.info(`Instrumenting ${moduleName}`);
36
36
  if (!mongodb) {
37
37
  return;
38
38
  }
@@ -18,7 +18,7 @@ const symbols = {
18
18
  createPoolCluster: Symbol('createPoolCluster'),
19
19
  wrappedPoolConnection: Symbol('wrappedPoolConnection')
20
20
  }
21
-
21
+ let modName = '';
22
22
  module.exports = initialize;
23
23
 
24
24
  /**
@@ -28,6 +28,7 @@ module.exports = initialize;
28
28
  * @param {*} moduleName
29
29
  */
30
30
  function initialize(shim, mysql, moduleName) {
31
+ modName = moduleName;
31
32
  logger.info("Instrumenting", moduleName);
32
33
  if (shim._moduleRoot.includes("mysql2")) {
33
34
  promiseInitialize(shim, mysql, moduleName);
@@ -119,6 +120,7 @@ function wrapGetConnection(shim, connectable) {
119
120
 
120
121
  const proto = Object.getPrototypeOf(connectable);
121
122
  shim.wrap(proto, "getConnection", function doWrapGetConnection(shim, fn) {
123
+ logger.debug(`Instrumenting ${modName}.getConnection`);
122
124
  return function wrappedGetConnection() {
123
125
  const args = shim.toArray(arguments);
124
126
  const cbIdx = args.length - 1;
@@ -208,7 +210,7 @@ function extractQueryArgs(shim, args) {
208
210
  // query(opts [, values], callback)
209
211
  rawQuery = args[0].sql;
210
212
  try {
211
- if (args[0].values && typeof args[0].values == 'object') {
213
+ if (args[0].values && typeof args[0].values == 'object') {
212
214
  param = Object.values(args[0].values)
213
215
  }
214
216
  } catch (error) {
@@ -240,6 +242,7 @@ function extractQueryArgs(shim, args) {
240
242
  */
241
243
  function queryHook(shim, mod, method, isPoolQuery) {
242
244
  shim.wrap(mod, method, function makeQueryWrapper(shim, fn) {
245
+ logger.debug(`Instrumenting ${modName}.${method}`);
243
246
  return function queryWrapper() {
244
247
  const parameters = extractQueryArgs(shim, arguments);
245
248
  shim.interceptedArgs = parameters;
@@ -26,6 +26,7 @@ function initialize(shim, childProcess, moduleName) {
26
26
  */
27
27
  function execFileHook(shim, childProcess, moduleName) {
28
28
  shim.wrap(childProcess, "execFile", function makeWrapper(shim, fn) {
29
+ logger.debug(`Instrumenting ${moduleName}.execFile`);
29
30
  return function wrapper() {
30
31
  const interceptedArgs = [arguments[0]];
31
32
  shim.interceptedArgs = interceptedArgs;
@@ -54,6 +55,7 @@ function execFileHook(shim, childProcess, moduleName) {
54
55
  */
55
56
  function spawnSyncHook(shim, childProcess, moduleName) {
56
57
  shim.wrap(childProcess, "spawn", function makeWrapper(shim, fn) {
58
+ logger.debug(`Instrumenting ${moduleName}.spawnSync`);
57
59
  return function wrapper() {
58
60
  const interceptedArgs = arguments[0].args;
59
61
  shim.interceptedArgs = interceptedArgs;
@@ -83,6 +85,7 @@ function spawnSyncHook(shim, childProcess, moduleName) {
83
85
  */
84
86
  function spawnHook(shim, childProcess, moduleName) {
85
87
  shim.wrap(childProcess, "spawn", function makeWrapper(shim, fn) {
88
+ logger.debug(`Instrumenting ${moduleName}.spawn`);
86
89
  return function wrapper() {
87
90
  let param = [];
88
91
  param.push(arguments[0]);
@@ -66,6 +66,7 @@ module.exports = function initialize(shim, pgsql, moduleName) {
66
66
 
67
67
  function queryHook(shim, mod, method){
68
68
  shim.wrap(mod, method, function makeQueryWrapper(shim, fn){
69
+ logger.debug(`Instrumenting pg.${method}`);
69
70
  return function queryWrapper(){
70
71
  const request = requestManager.getRequest(shim);
71
72
  const sqlArgs = [];
@@ -21,12 +21,13 @@ const logger = API.getLogger();
21
21
  * @param {*} moduleName
22
22
  */
23
23
  function initialize(shim, mod, moduleName) {
24
- logger.info('Instrumenting Restify');
24
+ logger.info(`Instrumenting ${moduleName}`);
25
25
  try {
26
26
  const registryRadix = shim.require("./lib/routerRegistryRadix");
27
27
 
28
28
  //routerRadix hook
29
29
  shim.wrap(registryRadix && registryRadix.prototype, 'add', function makeWrapper(shim, fn) {
30
+ logger.info(`Instrumenting registryRadix.prototype.add`);
30
31
  return function wrapper() {
31
32
  if (arguments[0]) {
32
33
  try {
@@ -47,6 +48,7 @@ function initialize(shim, mod, moduleName) {
47
48
  const router = shim.require("./lib/router");
48
49
 
49
50
  shim.wrap(router && router.prototype, 'lookup', function lookupWrapper(shim, fn) {
51
+ logger.info(`Instrumenting router.prototype.lookup`);
50
52
  return function wrappedLookup() {
51
53
  const result = fn.apply(this, arguments);
52
54
  extractParams(shim, arguments[0]);
@@ -27,7 +27,7 @@ function initialize(shim, mod, moduleName){
27
27
  xpathHooks(shim, mod, 'useNamespaces', moduleName);
28
28
  }
29
29
  else if(moduleName==='xpath.js'){
30
- wrappedXPathDotJS(shim, mod);
30
+ wrappedXPathDotJS(shim, mod, moduleName);
31
31
  }
32
32
 
33
33
  }
@@ -40,6 +40,7 @@ function initialize(shim, mod, moduleName){
40
40
  */
41
41
  function xpathHooks(shim, mod, methodName, moduleName){
42
42
  shim.wrap(mod, methodName, function makeWrapper(shim,fn){
43
+ logger.debug(`Instrumenting ${moduleName}.${methodName}`);
43
44
  return function wrapper(){
44
45
  const interceptedArgs = [arguments[0]];
45
46
  shim.interceptedArgs = interceptedArgs;
@@ -67,8 +68,9 @@ function xpathHooks(shim, mod, methodName, moduleName){
67
68
  * @param {*} shim
68
69
  * @param {*} mod
69
70
  */
70
- function wrappedXPathDotJS(shim, mod){
71
+ function wrappedXPathDotJS(shim, mod, moduleName){
71
72
  const wrappedExport = shim.wrapExport(mod, function wrapXpathJSModule(shim, fn) {
73
+ logger.debug(`Instrumenting ${moduleName}`);
72
74
  return function wrappedXpathJSModule() {
73
75
  const interceptedArgs = [arguments[1]];
74
76
  shim.interceptedArgs = interceptedArgs;
@@ -181,6 +181,15 @@ newrelic.instrumentWebframework({
181
181
  }
182
182
  })
183
183
 
184
+ newrelic.instrumentWebframework({
185
+ moduleName: '@nestjs/core',
186
+ isEsm: true,
187
+ onRequire: require('./hooks/@nestjs/nr-nestjs-core'),
188
+ onError: function intrumentErrorHandler(err) {
189
+ logger.error(err.message, err.stack)
190
+ }
191
+ })
192
+
184
193
 
185
194
 
186
195
 
@@ -14,6 +14,9 @@ const agentConfig = require('./resources/config');
14
14
  const shaSizeUtil = require('./lib/core/sha-size-util');
15
15
  const njsAgentConstants = require('./lib/core/sec-agent-constants');
16
16
 
17
+ const NRLogger = API.newrelic.shim.logger.child({ component: 'security-agent' });
18
+ NRLogger.info("Starting New Relic Node.js Security Agent ");
19
+
17
20
  const {
18
21
  IS_LAMBDA_ENV,
19
22
  AWS_LAMBDA_FUNCTION_NAME_ENV_IDENTIFIER,
@@ -106,6 +109,7 @@ function initialize() {
106
109
  initLogger.info("[STEP-3] => Gathering information about the application :", JSON.stringify(applicationInfo));
107
110
  if (NRAgent) {
108
111
  NRAgent.on('started', () => {
112
+ NRLogger.info("Started New Relic Node.js Security Agent");
109
113
  if (NRAgent.config.security.enabled || !NRAgent.config.high_security) {
110
114
  wsClient.initialize();
111
115
  logger.info('NR Agent started with agent_run_id:', NRAgent.config.run_id, NRAgent.config.account_id);
@@ -113,7 +117,8 @@ function initialize() {
113
117
  Agent.setNRAgent(NRAgent);
114
118
  }
115
119
  else {
116
- logger.info("security.enabled flag is set to false")
120
+ logger.warn("security.enabled flag is set to false");
121
+ NRLogger.warn("Failed to Start New Relic Node.js Security Agent. Please check security.enabled and high_security flags");
117
122
  }
118
123
  });
119
124
  }
@@ -131,9 +136,9 @@ function initialize() {
131
136
  }
132
137
  }
133
138
  logger.info(LOG_MESSAGES.AGENT_INFO, applicationInfo.collectorVersion, applicationInfo.jsonVersion, applicationInfo.buildNumber);
134
-
139
+
135
140
  applicationInfoModule.addProperty('serverInfo.deployedApplications', applications);
136
-
141
+
137
142
  shaSizeUtil.setLogger(logger);
138
143
 
139
144
  // Initialize Agent Status
@@ -159,7 +164,7 @@ function getProcCMDLineSanitized(applicationInfoModule) {
159
164
  }
160
165
 
161
166
  (() => {
162
- if(!NRAgent.config.security.enabled){
167
+ if (!NRAgent.config.security.enabled) {
163
168
  logger.warn("security.enabled flag is set to false");
164
169
  return;
165
170
  }
@@ -9,6 +9,8 @@ const appInfo = require('./applicationinfo').getInstance();
9
9
  const logs = require('./logging');
10
10
  const logger = logs.getLogger();
11
11
  const initLogger = logs.getInitLogger();
12
+ const LinkingMetaData = require('./LinkingMetadata');
13
+ const LogMessage = require('./LogMessage');
12
14
 
13
15
 
14
16
  /* eslint-disable camelcase */
@@ -20,6 +22,7 @@ function AuthHeaders() {
20
22
  try {
21
23
  const NRAgent = API.getNRAgent();
22
24
  authHeaders = {}
25
+ const metadata = LinkingMetaData.getLinkingMetadata();
23
26
  authHeaders['NR-CSEC-CONNECTION-TYPE'] = 'LANGUAGE_COLLECTOR';
24
27
  authHeaders['NR-AGENT-RUN-TOKEN'] = NRAgent.config.run_id;
25
28
  const COLLECTOR_VERSION = appInfo.collectorVersion || '';
@@ -32,8 +35,12 @@ function AuthHeaders() {
32
35
  authHeaders['NR-ACCOUNT-ID'] = NRAgent.config.account_id;
33
36
  authHeaders['NR-LICENSE-KEY'] = NRAgent ? NRAgent.config.license_key : '';
34
37
  authHeaders["NR-CSEC-IAST-DATA-TRANSFER-MODE"] = "PULL";
38
+ authHeaders['NR-CSEC-ENTITY-NAME'] = metadata['entity.name'] ? metadata['entity.name'] : '';
39
+ authHeaders['NR-CSEC-ENTITY-GUID'] = metadata['entity.guid'] ? metadata['entity.guid'] : '';
35
40
  } catch (error) {
36
41
  logger.error("Exception in preparing auth headers:", error);
42
+ const logMessage = new LogMessage.logMessage("SEVERE", 'Exception in preparing auth headers', __filename, error);
43
+ commonUtils.addLogEventtoBuffer(logMessage);
37
44
  }
38
45
 
39
46
  return authHeaders;
@@ -6,6 +6,8 @@
6
6
  const LinkingMetaData = require('./LinkingMetadata');
7
7
  const API = require('../../../nr-security-api');
8
8
  const NRAgent = API.getNRAgent();
9
+ const { BasicInfo } = require('./event');
10
+ const applicationInfo = require('./applicationinfo').getInstance();
9
11
 
10
12
  /**
11
13
  * constructor function to create LogMessageException object
@@ -13,10 +15,22 @@ const NRAgent = API.getNRAgent();
13
15
  * @param {*} cause
14
16
  * @param {*} stacktrace
15
17
  */
16
- function logMessageException (exception) {
17
- this.message = exception.message;
18
- this.cause = exception.cause ? exception.cause : '';
19
- this.stacktrace = exception.stack;
18
+ function logMessageException(exception) {
19
+ let exceptionObject = {};
20
+ try {
21
+ exceptionObject.message = exception.message ? exception.message : '';
22
+ exceptionObject.cause = exception.cause ? exception.cause : '';
23
+ if (exception.stack) {
24
+ let stacktraceInList = exception.stack.split('\n');
25
+ stacktraceInList = stacktraceInList.slice(1);
26
+ exceptionObject.stacktrace = stacktraceInList;
27
+ }
28
+
29
+ } catch (error) {
30
+
31
+ }
32
+
33
+ return exceptionObject;
20
34
  }
21
35
 
22
36
  /**
@@ -28,17 +42,28 @@ function logMessageException (exception) {
28
42
  * @param {*} exception
29
43
  * @param {*} linkingMetadata
30
44
  */
31
- function logMessage (level, message, caller, exception) {
32
- this.timestamp = Date.now();
33
- this.level = level;
34
- this.message = message;
35
- this.caller = caller;
36
- if (exception) {
37
- this.exception = logMessageException(exception);
38
- }
39
- if (NRAgent) {
40
- this.linkingMetadata = LinkingMetaData.getLinkingMetadata();
45
+ function logMessage(level, message, caller, exception) {
46
+ try {
47
+ BasicInfo.call(this);
48
+ this.jsonVersion = applicationInfo.jsonVersion;
49
+ this.jsonName = 'log-message';
50
+ this.applicationUUID = applicationInfo.applicationUUID;
51
+ this.groupName = applicationInfo.groupName;
52
+ this.policyVersion = applicationInfo.policyVersion;
53
+ this.timestamp = Date.now();
54
+ this.level = level ? level : 'SEVERE';
55
+ this.message = message;
56
+ this.caller = caller;
57
+ if (exception) {
58
+ this.exception = logMessageException(exception);
59
+ }
60
+ if (NRAgent) {
61
+ this.linkingMetadata = LinkingMetaData.getLinkingMetadata();
62
+ }
63
+ } catch (error) {
64
+
41
65
  }
66
+
42
67
  }
43
68
 
44
69
  module.exports = {
@@ -589,7 +589,7 @@ function setPodProperties (identifier, containerProp) {
589
589
  podProp.namespace = getPodNameSpace();
590
590
  podProp.name = getHostName();
591
591
  podProp.creationTimestamp = new Date().getTime();
592
- podProp.ContainerProperties.push(containerProp);
592
+ podProp.containerProperties.push(containerProp);
593
593
  logger.debug("podProp:", podProp);
594
594
  } catch (e) {
595
595
  logger.error(e);
@@ -12,16 +12,18 @@ 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 { LOG_MESSAGES, CSEC_HOME, SLASH } = require('./sec-agent-constants');
15
+ const { CSEC_HOME, SLASH } = require('./sec-agent-constants');
16
16
 
17
- const prettyBytes = require('pretty-bytes');
18
17
  const njsAgentConstants = require('./sec-agent-constants');
19
- const LinkingMetaData = require('./LinkingMetadata');
20
18
  const API = require('../../../nr-security-api');
21
19
  const NRAgent = API.getNRAgent();
22
20
  const lodash = require('lodash')
23
21
  const backupLogFileRegex = /.log.\d/gm;
24
22
  let websocketHealth;
23
+
24
+ const ringBuffer = require('ringbufferjs');
25
+ const bufferedLogEvents = new ringBuffer(10);
26
+
25
27
  /**
26
28
  * Utility to generate UUID
27
29
  * @returns UUID
@@ -417,6 +419,26 @@ function getWSHealthStatus() {
417
419
  }
418
420
  }
419
421
 
422
+ /**
423
+ * return buffered Log Event instance.
424
+ */
425
+ function getLogEvents() {
426
+ return bufferedLogEvents;
427
+ }
428
+
429
+
430
+ /**
431
+ * Utility to add error object in error buffer
432
+ * @param {*} error
433
+ */
434
+ function addLogEventtoBuffer(logMessage) {
435
+ try {
436
+ bufferedLogEvents.enq(logMessage);
437
+ } catch (error) {
438
+ logger.debug(error);
439
+ }
440
+ }
441
+
420
442
  module.exports = {
421
443
  getUUID,
422
444
  getKindIdPair,
@@ -438,5 +460,7 @@ module.exports = {
438
460
  getCSECmode,
439
461
  getPathOfCACert,
440
462
  getWSHealthStatus,
441
- setWSHealthStatus
463
+ setWSHealthStatus,
464
+ addLogEventtoBuffer,
465
+ getLogEvents
442
466
  };
@@ -9,14 +9,16 @@ 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, CSEC_SEP } = require('../../../sec-agent-constants');
12
+ const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP, SEVERE } = 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 LogMessage = require('../../../LogMessage');
18
19
 
19
20
  require('dns').setDefaultResultOrder('ipv4first')
21
+ const PolicyManager = require('../../../Policy');
20
22
 
21
23
  const {
22
24
  IS_LAMBDA_ENV,
@@ -42,6 +44,16 @@ function startIASTSchedular() {
42
44
  if (iastIntervalConst) {
43
45
  clearInterval(iastIntervalConst);
44
46
  }
47
+ let probingInterval = 5;
48
+
49
+ const policyInstance = PolicyManager.getInstance();
50
+ if (policyInstance.data) {
51
+ probingInterval = policyInstance.data.vulnerabilityScan.iastScan.probing.interval;
52
+ }
53
+ if (isNaN(probingInterval)) {
54
+ probingInterval = 5;
55
+ }
56
+
45
57
  iastIntervalConst = setInterval(() => {
46
58
  let data = IASTUtil.generateIASTDataRequest();
47
59
  let currentTime = Date.now();
@@ -55,7 +67,7 @@ function startIASTSchedular() {
55
67
  if (timeDiffInSeconds > 5 && additionalCoolDownTime == 0) {
56
68
  Agent.getAgent().client.dispatcher(data);
57
69
  }
58
- }, 5000);
70
+ }, probingInterval * 1000);
59
71
  }
60
72
 
61
73
  /**
@@ -65,7 +77,7 @@ function startIASTSchedular() {
65
77
  function logScannedApiId(fuzzHeader, requestURL) {
66
78
  try {
67
79
  let apiId = fuzzHeader.split(CSEC_SEP)[0]
68
- if (apiId && !fuzzedApiIDSet.has(apiId) ) {
80
+ if (apiId && !fuzzedApiIDSet.has(apiId)) {
69
81
  fuzzedApiIDSet.add(apiId);
70
82
  logger.info("IAST Scan for API %s with ID : %s started.", requestURL, apiId);
71
83
  }
@@ -87,6 +99,9 @@ function handler(json) {
87
99
  IASTUtil.addPendingRequestId(json.id);
88
100
  } catch (error) {
89
101
  logger.error('Parsing exeception in fuzz request: ', error);
102
+
103
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Parsing exeception in fuzz request', __filename, error);
104
+ commonUtils.addLogEventtoBuffer(logMessage);
90
105
  }
91
106
  if (!fuzzRequest) {
92
107
  return;
@@ -132,10 +147,10 @@ function handleFuzzRequest(fuzzDetails) {
132
147
 
133
148
  IASTUtil.completedRequestsMapInit(fuzzRequest.id);
134
149
 
135
- if(fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]){
150
+ if (fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
136
151
  logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
137
152
  }
138
-
153
+
139
154
 
140
155
  logger.info('Firing http request:: URL: ' + config.url);
141
156
 
@@ -181,7 +196,7 @@ function handleFuzzResponse(response, fuzzDetails) {
181
196
  function parseAxiosHttpRequestToFuzz(requestObject) {
182
197
  let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
183
198
  let host = serverName + COLON + requestObject.serverPort;
184
- if(requestObject.headers && requestObject.headers['content-length']){
199
+ if (requestObject.headers && requestObject.headers['content-length']) {
185
200
  delete requestObject.headers['content-length'];
186
201
  }
187
202
  return {
@@ -5,7 +5,7 @@
5
5
 
6
6
  const _ = require('lodash');
7
7
 
8
- const { SERVER_COMMAND } = require('../../../sec-agent-constants');
8
+ const { SERVER_COMMAND, SEVERE } = require('../../../sec-agent-constants');
9
9
  const UnsupportedAgentHandler = require('./unsupported-agent-handler');
10
10
  const StartupHandshakeHandler = require('./startup-handshake-handler');
11
11
  const FuzzRequestHandler = require('./fuzz-request-handler');
@@ -17,6 +17,7 @@ const coolDown = require('./iast-cool-down');
17
17
  const recordDeletion = require('./iast-record-deletion');
18
18
  const logs = require('../../../logging');
19
19
  let logger = logs.getLogger();
20
+ const LogMessage = require('../../../LogMessage');
20
21
 
21
22
  const commandHandlerMap = {
22
23
  [SERVER_COMMAND.STARTUP_WELCOME_MSG]: StartupHandshakeHandler,
@@ -38,7 +39,9 @@ const handle = function handle (json) {
38
39
  NoOpHandler.handler.call(this, json);
39
40
  }
40
41
  } catch (error) {
41
- logger.error("Error in handling Control Commnad",error);
42
+ logger.error("Error in handling Control Command",error);
43
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in handling Control Command', __filename, error);
44
+ commonUtils.addLogEventtoBuffer(logMessage);
42
45
  }
43
46
 
44
47
  };
@@ -17,10 +17,11 @@ const hc = require('../../health-check');
17
17
  const fs = require('fs');
18
18
  const { promisify } = require('../../sec-util');
19
19
  const ResponseHandler = require('./response');
20
- const { EMPTY_APPLICATION_UUID, LOG_MESSAGES, CSEC_SEP, NR_CSEC_FUZZ_REQUEST_ID, EXITEVENT, JSON_NAME, RASP } = require('../../sec-agent-constants');
20
+ const { EMPTY_APPLICATION_UUID, LOG_MESSAGES, CSEC_SEP, NR_CSEC_FUZZ_REQUEST_ID, EXITEVENT, JSON_NAME, RASP, SEVERE } = require('../../sec-agent-constants');
21
21
  const statusUtils = require('../../statusUtils');
22
22
  const commonUtils = require('../../commonUtils');
23
23
  const IASTUtil = require('../websocket/response/IASTUtils');
24
+ const LogMessage = require('../../LogMessage');
24
25
 
25
26
  const BUSY = 'busy';
26
27
  const IDLE = 'idle';
@@ -109,9 +110,12 @@ SecWebSocket.prototype.init = function init() {
109
110
  cert = fs.readFileSync(certPath, 'utf8');
110
111
  } catch (error) {
111
112
  logger.error("Error in reading certificate:", error);
113
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in reading certificate for websocket', __filename, error);
114
+ commonUtils.addLogEventtoBuffer(logMessage);
115
+
112
116
  }
113
117
 
114
- webSocket = new WebSocket(validatorService, null, { headers: authHeaders, cert: cert, handshakeTimeout: 10000 });
118
+ webSocket = new WebSocket(validatorService, { headers: authHeaders, cert: cert, handshakeTimeout: 10000 });
115
119
 
116
120
  webSocket.on(WS_ON_OPEN, this.openCB);
117
121
  webSocket.on(WS_ON_MSG, this.msgCB);
@@ -120,6 +124,17 @@ SecWebSocket.prototype.init = function init() {
120
124
  lastPongtime = Date.now();
121
125
  commonUtils.setWSHealthStatus('OK');
122
126
  logger.debug("Pong received from Validator");
127
+
128
+ try {
129
+ let logBuffer = commonUtils.getLogEvents();
130
+ while (logBuffer.size() > 0) {
131
+ let logEvent = logBuffer.deq();
132
+ this.dispatch(logEvent);
133
+ }
134
+ } catch (error) {
135
+ logger.error("Error while sending log events", error);
136
+ }
137
+
123
138
  })
124
139
  webSocket.on('close', () => {
125
140
  logger.warn("WS connection closed");
@@ -172,6 +187,7 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
172
187
  SecWebSocket.prototype.pingWS = async function pingWS() {
173
188
  const self = this;
174
189
  let currentTime = Date.now();
190
+
175
191
  let timeDiffInPing = (currentTime - lastPongtime) / 1000;
176
192
  if (timeDiffInPing > 40 && lastPongtime != 0) {
177
193
  self.obeyReconnect();
@@ -193,6 +209,7 @@ SecWebSocket.prototype.pingWS = async function pingWS() {
193
209
  commonUtils.setWSHealthStatus('Error');
194
210
  return;
195
211
  }
212
+
196
213
  }
197
214
 
198
215
  /**
@@ -412,6 +429,8 @@ const defaultOnErrCB = () => function onClose(error) {
412
429
  statusUtils.addErrortoBuffer(error);
413
430
  const self = this;
414
431
  logger.error("Error while connecting to validator:", error.message)
432
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error while connecting to validator', __filename, error);
433
+ commonUtils.addLogEventtoBuffer(logMessage);
415
434
  logger.warn("Security Agent INACTIVE!!!")
416
435
  if (error && error.code && (error.code === 'ECONNREFUSED' || error.code === 'EPIPE' || error.code === 'ECONNRESET')) {
417
436
  logger.debug("WS Client Error callback called:", error);
@@ -108,6 +108,9 @@ module.exports = {
108
108
  CSEC_SEP: ':IAST:',
109
109
  RASP: 'RASP',
110
110
  IAST: 'IAST',
111
+ SEVERE: 'SEVERE',
112
+ HIGH: 'HIGH',
113
+ CRITICAL: 'CRITICAL',
111
114
 
112
115
  LOG_MESSAGES: {
113
116
  LOADED_CSEC_ENVS_MSG: '[STEP-1][COMPLETE][env] Environment Information Gathering Done.',
@@ -9,10 +9,12 @@ 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 } = require('./sec-agent-constants');
12
+ const { LOG_MESSAGES, NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, RASP, SEVERE } = 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');
16
+ const LogMessage = require('./LogMessage');
17
+ const commonUtils = require('./commonUtils');
16
18
 
17
19
  const statusUtils = require('./statusUtils');
18
20
 
@@ -67,10 +69,10 @@ function generateSecEvent(securityMetadata) {
67
69
  const traceObject = securityMetadata.traceObject;
68
70
  const request = securityMetadata.request;
69
71
 
70
- if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
72
+ if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
71
73
  HC.getInstance().iastEventStats.processed++;
72
74
  }
73
- else{
75
+ else {
74
76
  HC.getInstance().raspEventStats.processed++;
75
77
  }
76
78
  try {
@@ -102,11 +104,14 @@ function generateSecEvent(securityMetadata) {
102
104
  } catch (e) {
103
105
  statusUtils.addErrortoBuffer(e);
104
106
  logger.error("Error in generating event:", e);
105
-
106
- if(request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
107
+
108
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error while generating event', __filename, e);
109
+ commonUtils.addLogEventtoBuffer(logMessage);
110
+
111
+ if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
107
112
  HC.getInstance().iastEventStats.errorCount++;
108
113
  }
109
- else{
114
+ else {
110
115
  HC.getInstance().raspEventStats.errorCount++;
111
116
  }
112
117
  }
@@ -141,6 +146,9 @@ function generateExitEvent(secEvent) {
141
146
  }
142
147
  } catch (error) {
143
148
  logger.error('Error in generating exit event', error);
149
+
150
+ const logMessage = new LogMessage.logMessage(SEVERE, 'Error in generating exit event', __filename, error);
151
+ commonUtils.addLogEventtoBuffer(logMessage);
144
152
  HC.getInstance().exitEventStats.errorCount++;
145
153
  }
146
154
  }
@@ -140,17 +140,18 @@ const sendHC = async function () {
140
140
  * @param {JSON} eventJson
141
141
  */
142
142
  function getDispatcherAndSendEvent(eventJson) {
143
- const eventType = eventJson.jsonName;
144
- if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
145
- hc.getInstance().registerEventProcessed();
146
- }
143
+ if (eventJson) {
144
+ const eventType = eventJson.jsonName;
145
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
146
+ hc.getInstance().registerEventProcessed();
147
+ }
147
148
 
148
- dispatcher(eventJson);
149
+ dispatcher(eventJson);
149
150
 
150
- if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
151
- hc.getInstance().registerEventSent();
151
+ if (eventType === JSON_NAME.EVENT || eventType === EXITEVENT) {
152
+ hc.getInstance().registerEventSent();
153
+ }
152
154
  }
153
-
154
155
  }
155
156
 
156
157
  /**
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
- "jsonVersion": "1.1.0",
6
+ "jsonVersion": "1.1.1",
7
7
  "contributors": [
8
8
  {
9
9
  "name": "Sumit Suthar",
@@ -35,9 +35,9 @@
35
35
  "url": "https://github.com/newrelic/csec-node-agent.git"
36
36
  },
37
37
  "dependencies": {
38
- "@aws-sdk/client-lambda": "^3.405.0",
39
- "axios": "1.6.0",
40
- "check-disk-space": "3.3.1",
38
+ "@aws-sdk/client-lambda": "^3.436.0",
39
+ "axios": "^1.6.3",
40
+ "check-disk-space": "^3.4.0",
41
41
  "content-type": "^1.0.5",
42
42
  "fast-safe-stringify": "^2.1.1",
43
43
  "find-package-json": "^1.2.0",
@@ -56,7 +56,7 @@
56
56
  "unescape": "^1.0.1",
57
57
  "unescape-js": "^1.1.4",
58
58
  "uuid": "^9.0.1",
59
- "ws": "^7.5.9"
59
+ "ws": "^8.14.2"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@hapi/hapi": "^21.3.0",