@newrelic/security-agent 2.3.1 → 2.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,17 @@
1
+ ### v2.4.0 (2025-03-21)
2
+ #### Features
3
+ * Support for gRPC error reporting
4
+ * Support to detect API endpoints in next.js framework
5
+ #### Miscellaneous chores
6
+ * Updated axios to v1.8.4
7
+
8
+ ### v2.3.2 (2025-03-11)
9
+ #### Bug fixes
10
+ * Updated axios to v1.8.2
11
+ #### Miscellaneous chores
12
+ * Updated readme
13
+
14
+
1
15
  ### v2.3.1 (2025-02-04)
2
16
  #### Bug fixes
3
17
  * Removed docker-cli-js dependency and updated mongodb unit test case (#283)
package/README.md CHANGED
@@ -51,6 +51,7 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
51
51
  - `https`
52
52
  - `fs`
53
53
  - `child_process`
54
+ - `vm`
54
55
  - [mysql](https://www.npmjs.com/package/mysql)(2.16.x and above)
55
56
  - [mysql2](https://www.npmjs.com/package/mysql2) (2.x and above)
56
57
  - [pg](https://www.npmjs.com/package/pg)(7.x and above)
@@ -68,6 +69,7 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
68
69
  - [xpath](https://www.npmjs.com/package/xpath)(0.0.20 and above)
69
70
  - [xpath.js](https://www.npmjs.com/package/xpath.js)(0.0.1 and above)
70
71
  - [undici](https://www.npmjs.com/package/undici)(4.7.0 and above)
72
+ - [next](https://www.npmjs.com/package/next)(13.4.19 and above)
71
73
 
72
74
  For more information, please see New Relic Node.js agent [compatibility and requirements](https://docs.newrelic.com/docs/apm/agents/nodejs-agent/getting-started/compatibility-requirements-nodejs-agent/).
73
75
 
@@ -78,7 +78,7 @@ code, the source code can be found at [https://github.com/newrelic/csec-node-age
78
78
 
79
79
  ### axios
80
80
 
81
- This product includes source derived from [axios](https://github.com/axios/axios) ([v1.7.9](https://github.com/axios/axios/tree/v1.7.9)), distributed under the [MIT License](https://github.com/axios/axios/blob/v1.7.9/LICENSE):
81
+ This product includes source derived from [axios](https://github.com/axios/axios) ([v1.8.4](https://github.com/axios/axios/tree/v1.8.4)), distributed under the [MIT License](https://github.com/axios/axios/blob/v1.8.4/LICENSE):
82
82
 
83
83
  ```
84
84
  # Copyright (c) 2014-present Matt Zabriskie & Collaborators
@@ -16,6 +16,7 @@ const grpcutils = require('../../core/grpc-utils');
16
16
  const routeManager = require('../../core/route-manager');
17
17
  let serverAddress;
18
18
  const ExceptionReporting = require('../../../nr-security-agent/lib/core/ExceptionReporting');
19
+ const responseErrorSet = new Set([2, 12, 13, 14, 15])
19
20
 
20
21
 
21
22
 
@@ -58,6 +59,33 @@ module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
58
59
  shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
59
60
  }
60
61
 
62
+ /**
63
+ * Wrapper for error collection
64
+ * @param {*} shim
65
+ * @param {*} call
66
+ */
67
+ module.exports.wrapCall = function wrappedCall(shim, call) {
68
+ shim.wrap(call, 'callErrorFromStatus', function wrapCallErrorFromStatus(shim, fn) {
69
+ if (!shim.isFunction(fn)) {
70
+ return fn
71
+ }
72
+ return function wrappedCallErrorFromStatus() {
73
+ const errRes = fn.apply(this, arguments);
74
+ try {
75
+ let error = new Error(errRes.message);
76
+ error.stack = errRes.stack;
77
+ const request = requestManager.getRequest(shim);
78
+ if (request) {
79
+ ExceptionReporting.generate5xxReportingEvent(error, request);
80
+ }
81
+ } catch (error) {
82
+ }
83
+
84
+ return errRes;
85
+ }
86
+ })
87
+ }
88
+
61
89
  module.exports.wrapServer = function wrappedServer(shim, server) {
62
90
  const grpcVersion = shim.require('./package.json').version
63
91
  logger.info("Instrumenting @grpc/grpc-js", grpcVersion)
@@ -109,6 +137,7 @@ function wrapStart(shim, original) {
109
137
  "path": method,
110
138
  "protocol": protocol,
111
139
  }
140
+
112
141
  if (request) {
113
142
  const traceObject = secUtils.getTraceObject(shim);
114
143
  const secMetadata = securityMetaData.getSecurityMetaData(request, urlObj, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
@@ -256,11 +285,11 @@ function wrapCallback(shim, mod, method) {
256
285
  if (arguments[0].details && request) {
257
286
  ExceptionReporting.generateExceptionReportingEvent(arguments[0].details, request);
258
287
  }
259
- //TODO GRPC response errors reporting
260
- // if (arguments[0].code && arguments[0].code > 0 && request) {
261
- // let responseCode = arguments[0].code
262
- // ExceptionReporting.generate5xxReportingEvent(null, request, responseCode);
263
- // }
288
+
289
+ if (arguments[0].code && responseErrorSet.has(arguments[0].code) && request) {
290
+ let responseCode = arguments[0].code
291
+ ExceptionReporting.generate5xxReportingEvent(null, request, responseCode);
292
+ }
264
293
  }
265
294
  } catch (error) {
266
295
 
@@ -282,7 +311,7 @@ function wrapCallback(shim, mod, method) {
282
311
  function addRequestData(shim, type, path, args) {
283
312
  const data = Object.assign({});
284
313
  try {
285
- const transaction = shim.tracer.getTransaction();
314
+ const transaction = shim.tracer.getTransaction();
286
315
  if (transaction) {
287
316
  data.method = path;
288
317
  data.url = path;
@@ -6,9 +6,14 @@
6
6
  'use strict'
7
7
  const requestManager = require("../../core/request-manager");
8
8
  const API = require("../../../nr-security-api");
9
+ const routeManager = require('../../core/route-manager');
9
10
  const logger = API.getLogger();
11
+ const lodash = require('lodash');
12
+ const { ATTHERATE, DOUBLE_DOLLAR, EMPTY_STR } = require('../../core/constants');
10
13
 
11
14
  const semver = require('semver')
15
+ const fs = require('fs');
16
+ const path = require('path');
12
17
 
13
18
  module.exports = function initialize(shim, nextjs) {
14
19
  const nextVersion = shim.require('./package.json').version
@@ -16,6 +21,23 @@ module.exports = function initialize(shim, nextjs) {
16
21
 
17
22
  const nextServer = nextjs.default;
18
23
  hookRunAPI(shim, nextServer.prototype, nextVersion);
24
+
25
+ //TODO need to update for API endpoints
26
+ try {
27
+ const appRoot = process.env.PWD;
28
+ const searchPath = appRoot + '/.next/server/pages/api';
29
+ const allAPIEndpoints = getAllAPIEndpoints(searchPath);
30
+ logger.debug("allAPIEndpoints:", allAPIEndpoints);
31
+ for (let index = 0; index < allAPIEndpoints.length; index++) {
32
+ const element = allAPIEndpoints[index];
33
+ let key = "*" + ATTHERATE + element;
34
+ routeManager.setRoute(key, EMPTY_STR);
35
+ }
36
+ } catch (error) {
37
+ logger.debug("Error while getting all API end points for next.js", error);
38
+ }
39
+
40
+
19
41
  }
20
42
 
21
43
  /**
@@ -91,3 +113,33 @@ function extractParams(shim, page, params) {
91
113
  }
92
114
  }
93
115
 
116
+
117
+ /**
118
+ * Utility to scan pages directory to get all avaible routes
119
+ * @param {*} dir
120
+ * @returns
121
+ */
122
+ const getAllAPIEndpoints = (dir) => {
123
+ logger.debug("dir is:", dir)
124
+ const apiEndpoints = [];
125
+
126
+ const scanDirectory = (currentDir) => {
127
+ logger.debug("currentDir is:", currentDir)
128
+ const files = fs.readdirSync(currentDir);
129
+
130
+ files.forEach((file) => {
131
+ const filePath = path.join(currentDir, file);
132
+ const isDirectory = fs.statSync(filePath).isDirectory();
133
+
134
+ if (isDirectory) {
135
+ scanDirectory(filePath);
136
+ } else {
137
+ const apiEndpoint = filePath.replace(`${dir}/`, '/api/').replace(/\.js*.*/, '');
138
+ apiEndpoints.push(apiEndpoint);
139
+ }
140
+ });
141
+ };
142
+
143
+ scanDirectory(dir);
144
+ return apiEndpoints;
145
+ };
@@ -235,6 +235,15 @@ newrelic.instrument({
235
235
  }
236
236
  })
237
237
 
238
+ newrelic.instrument({
239
+ moduleName: '@grpc/grpc-js/build/src/call',
240
+ isEsm: true,
241
+ onRequire: require('./hooks/grpc-js/nr-grpc').wrapCall,
242
+ onError: function intrumentErrorHandler(err) {
243
+ logger.error(err.message, err.stack)
244
+ }
245
+ })
246
+
238
247
  newrelic.instrument({
239
248
  moduleName: 'graphql/execution/execute',
240
249
  isEsm: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/security-agent",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "New Relic Security Agent for Node.js",
5
5
  "main": "index.js",
6
6
  "jsonVersion": "1.2.9",
@@ -39,7 +39,7 @@
39
39
  "url": "https://github.com/newrelic/csec-node-agent.git"
40
40
  },
41
41
  "dependencies": {
42
- "axios": "^1.7.9",
42
+ "axios": "^1.8.4",
43
43
  "check-disk-space": "^3.4.0",
44
44
  "content-type": "^1.0.5",
45
45
  "cron": "^3.1.7",