@newrelic/security-agent 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +3 -1
- package/lib/instrumentation-security/core/grpc-utils.js +53 -0
- package/lib/instrumentation-security/core/route-manager.js +28 -5
- package/lib/instrumentation-security/core/sec-utils.js +3 -3
- package/lib/instrumentation-security/hooks/express/nr-express.js +2 -4
- package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +284 -0
- package/lib/instrumentation-security/hooks/undici/nr-undici.js +139 -0
- package/lib/instrumentation-security/index.js +56 -0
- package/lib/nr-security-agent/lib/core/apiEndpoints.js +29 -0
- package/lib/nr-security-agent/lib/core/commonUtils.js +4 -4
- package/lib/nr-security-agent/lib/core/connections/grpcClient.js +111 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +31 -11
- package/lib/nr-security-agent/lib/core/logging.js +5 -5
- package/lib/nr-security-agent/lib/core/statusUtils.js +2 -2
- package/lib/nr-security-agent/lib/core/websocket-client.js +10 -1
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
### v1.1.0 (2024-03-19)
|
|
2
|
+
#### Features
|
|
3
|
+
* IAST support for grpc
|
|
4
|
+
* Functionality to report API endpoints of the application
|
|
5
|
+
* IAST support for undici
|
|
6
|
+
#### Bug fixes
|
|
7
|
+
* Updated permissions for file/directory created by security agent
|
|
8
|
+
#### Miscellaneous chores
|
|
9
|
+
* Bumped follow-redirects from v1.15.2 to v1.15.4
|
|
10
|
+
* Updated axios to v1.6.8
|
|
11
|
+
* Bumped ip from v2.0.0 to v2.0.1
|
|
12
|
+
* Bumped undici from 5.28.2 to v5.28.3
|
|
13
|
+
* Readme update
|
|
14
|
+
|
|
1
15
|
### v1.0.1 (2024-02-07)
|
|
2
16
|
#### Bug fixes
|
|
3
17
|
* Reporting of framework in security event json
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
**Note**: The IAST capability should only be used in pre-production environments as the application is tested for real exploitable vulnerabilities.
|
|
5
5
|
|
|
6
|
-
The New Relic security agent
|
|
6
|
+
The New Relic security agent enables instrumentation of a Node.js application for Interactive Application Security Testing(IAST) and exposes exploitable vulnerabilities.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -67,6 +67,8 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
|
|
|
67
67
|
- [ldapts](https://www.npmjs.com/package/ldapts)(2.x and above)
|
|
68
68
|
- [xpath](https://www.npmjs.com/package/xpath)(0.0.20 and above)
|
|
69
69
|
- [xpath.js](https://www.npmjs.com/package/xpath.js)(0.0.1 and above)
|
|
70
|
+
- [undici](https://www.npmjs.com/package/undici)(4.7.0 and above)
|
|
71
|
+
- [@grpc/grpc-js](https://www.npmjs.com/package/@grpc/grpc-js)(1.4.x and above)
|
|
70
72
|
|
|
71
73
|
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/).
|
|
72
74
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const grpcServiceMap = new Map();
|
|
8
|
+
const grpcMethodMap = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Utility to set service based on key value pair
|
|
11
|
+
* @param {*} key
|
|
12
|
+
* @param {*} value
|
|
13
|
+
*/
|
|
14
|
+
function setService(key, value){
|
|
15
|
+
grpcServiceMap.set(key,value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Utility to get service corresponding to key
|
|
20
|
+
* @param {*} key
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
function getService(key){
|
|
24
|
+
return grpcServiceMap.get(key);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Utility to set method object
|
|
29
|
+
* @param {*} key
|
|
30
|
+
* @param {*} value
|
|
31
|
+
*/
|
|
32
|
+
function setMethod(key, value){
|
|
33
|
+
grpcMethodMap.set(key,value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Utility to get method object
|
|
38
|
+
* @param {*} key
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
function getMethod(key){
|
|
42
|
+
return grpcMethodMap.get(key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports={
|
|
46
|
+
setService,
|
|
47
|
+
getService,
|
|
48
|
+
grpcServiceMap,
|
|
49
|
+
setMethod,
|
|
50
|
+
getMethod,
|
|
51
|
+
grpcMethodMap
|
|
52
|
+
|
|
53
|
+
}
|
|
@@ -10,8 +10,8 @@ const routeMap = new Map();
|
|
|
10
10
|
* @param {*} key
|
|
11
11
|
* @param {*} value
|
|
12
12
|
*/
|
|
13
|
-
function setRoute(key, value){
|
|
14
|
-
routeMap.set(key,value);
|
|
13
|
+
function setRoute(key, value) {
|
|
14
|
+
routeMap.set(key, value);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -19,12 +19,35 @@ function setRoute(key, value){
|
|
|
19
19
|
* @param {*} key
|
|
20
20
|
* @returns
|
|
21
21
|
*/
|
|
22
|
-
function getRoute(key){
|
|
22
|
+
function getRoute(key) {
|
|
23
23
|
return routeMap.get(key);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Utility to get all api end points
|
|
28
|
+
* @returns all keys from routeMap
|
|
29
|
+
*/
|
|
30
|
+
function getAllAPIEndPoints() {
|
|
31
|
+
let apiEndpoints = [];
|
|
32
|
+
try {
|
|
33
|
+
routeMap.forEach((value, key) => {
|
|
34
|
+
let obj = {};
|
|
35
|
+
let splitted = key.split('@');
|
|
36
|
+
obj.method = splitted[0];
|
|
37
|
+
obj.path = splitted[1];
|
|
38
|
+
obj.handler = routeMap.get(key);
|
|
39
|
+
apiEndpoints.push(obj);
|
|
40
|
+
})
|
|
41
|
+
} catch (error) {
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return apiEndpoints;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
27
49
|
getRoute,
|
|
28
50
|
setRoute,
|
|
29
|
-
routeMap
|
|
51
|
+
routeMap,
|
|
52
|
+
getAllAPIEndPoints
|
|
30
53
|
}
|
|
@@ -150,11 +150,11 @@ function getExecutionId(){
|
|
|
150
150
|
function createPathIfNotExist (dir) {
|
|
151
151
|
try {
|
|
152
152
|
if (!fs.existsSync(dir)) {
|
|
153
|
-
fs.mkdirSync(dir,
|
|
153
|
+
fs.mkdirSync(dir, 770, { recursive: true });
|
|
154
154
|
logger.info(dir + ' Created');
|
|
155
|
-
fs.chmodSync(dir,
|
|
155
|
+
fs.chmodSync(dir, 0o770);
|
|
156
156
|
} else {
|
|
157
|
-
fs.chmodSync(dir,
|
|
157
|
+
fs.chmodSync(dir, 0o770);
|
|
158
158
|
logger.debug(dir + ' Already Exists');
|
|
159
159
|
}
|
|
160
160
|
} catch (error) {
|
|
@@ -27,9 +27,8 @@ module.exports = function initialize(shim, express, moduleName) {
|
|
|
27
27
|
|
|
28
28
|
if (express.Router.use) {
|
|
29
29
|
wrapExpress4(shim, express)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
}
|
|
30
|
+
}
|
|
31
|
+
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
/**
|
|
@@ -57,7 +56,6 @@ function wrapExpress4(shim, express) {
|
|
|
57
56
|
}
|
|
58
57
|
})
|
|
59
58
|
|
|
60
|
-
|
|
61
59
|
//Wrapper to hook process_params method to extract out path param and query params.
|
|
62
60
|
shim.wrap(express.Router, 'process_params', function wrapParam(shim, fn) {
|
|
63
61
|
if (!shim.isFunction(fn)) {
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const requestManager = require("../../core/request-manager");
|
|
8
|
+
const secUtils = require("../../core/sec-utils");
|
|
9
|
+
const API = require("../../../nr-security-api");
|
|
10
|
+
const securityMetaData = require('../../core/security-metadata');
|
|
11
|
+
const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
|
|
12
|
+
const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
|
|
13
|
+
const logger = API.getLogger();
|
|
14
|
+
const semver = require('semver');
|
|
15
|
+
const grpcutils = require('../../core/grpc-utils');
|
|
16
|
+
let serverAddress;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
module.exports.initialize = function initialize(shim, grpc, moduleName) {
|
|
20
|
+
logger.info('Instrumenting grpc', moduleName);
|
|
21
|
+
const grpcVersion = shim.require('./package.json').version;
|
|
22
|
+
logger.debug("grpc version:", grpcVersion);
|
|
23
|
+
|
|
24
|
+
if (semver.lt(grpcVersion, '1.4.0')) {
|
|
25
|
+
logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (semver.gte(grpcVersion, '1.8.0')) {
|
|
30
|
+
const resolvingCall = shim.require('./build/src/resolving-call')
|
|
31
|
+
shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
32
|
+
} else {
|
|
33
|
+
const callStream = shim.require('./build/src/call-stream')
|
|
34
|
+
shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const server = shim.require('./build/src/server')
|
|
39
|
+
shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
|
|
40
|
+
shim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
41
|
+
|
|
42
|
+
//get all service
|
|
43
|
+
shim.wrap(server.Server.prototype, 'addService', wrapAddService);
|
|
44
|
+
|
|
45
|
+
//laod package def
|
|
46
|
+
const makeClient = shim.require('./build/src/make-client');
|
|
47
|
+
shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
module.exports.wrapStartResolve = function wrappedStartResolve(shim, resolvingCall) {
|
|
53
|
+
shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
|
|
57
|
+
shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports.wrapServer = function wrappedServer(shim, server) {
|
|
61
|
+
const grpcVersion = shim.require('./package.json').version
|
|
62
|
+
logger.info("Instrumenting @grpc/grpc-js", grpcVersion)
|
|
63
|
+
if (semver.lt(grpcVersion, '1.4.0')) {
|
|
64
|
+
logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
shim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
69
|
+
shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
|
|
70
|
+
//get all service
|
|
71
|
+
shim.wrap(server.Server.prototype, 'addService', wrapAddService);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports.wrapMakeClient = function wrapMakeClient(shim, makeClient) {
|
|
75
|
+
shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Instruments grpc-js client by intercepting the function that
|
|
81
|
+
* initiates client requests. This handles all four types of client
|
|
82
|
+
* invocations: unary, client-streaming, server-streaming, and
|
|
83
|
+
* bidirectional streaming.
|
|
84
|
+
*
|
|
85
|
+
* @param {object} shim the generic shim to instrument with
|
|
86
|
+
* @param {Function} original the original function
|
|
87
|
+
* @returns {Function} the instrumented function
|
|
88
|
+
*/
|
|
89
|
+
function wrapStart(shim, original) {
|
|
90
|
+
return function wrappedStart() {
|
|
91
|
+
const channel = this.channel;
|
|
92
|
+
const authorityName = (channel.target && channel.target.path) || channel.getDefaultAuthority;
|
|
93
|
+
// in 1.8.0 this changed from methodName to method
|
|
94
|
+
const method = this.methodName || this.method;
|
|
95
|
+
const protocol = 'grpc:';
|
|
96
|
+
const url = `${protocol}//${authorityName}${method}`;
|
|
97
|
+
logger.debug("grpc url is:", url)
|
|
98
|
+
const request = requestManager.getRequest(shim);
|
|
99
|
+
let urlObj = {
|
|
100
|
+
"headers":
|
|
101
|
+
{
|
|
102
|
+
"host": authorityName
|
|
103
|
+
},
|
|
104
|
+
"method": method,
|
|
105
|
+
"path": method,
|
|
106
|
+
"protocol": protocol,
|
|
107
|
+
}
|
|
108
|
+
if (request) {
|
|
109
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
110
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, urlObj, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
|
|
111
|
+
const secEvent = API.generateSecEvent(secMetadata);
|
|
112
|
+
API.sendEvent(secEvent);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const res = original.apply(this, arguments);
|
|
116
|
+
return res;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function wrapAddService(shim, original) {
|
|
123
|
+
return function wrappedbindAsync() {
|
|
124
|
+
const result = original.apply(this, arguments);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function wrapLoadPackageDefinition(shim, original) {
|
|
130
|
+
return function wrappedbindAsync() {
|
|
131
|
+
let serviceObject = arguments[0];
|
|
132
|
+
const result = original.apply(this, arguments);
|
|
133
|
+
iterateNestedObjectForServiceLookup(result, serviceObject);
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
function bindAsyncWrapper(shim, original) {
|
|
142
|
+
return function wrappedbindAsync() {
|
|
143
|
+
serverAddress = arguments[0];
|
|
144
|
+
shim.grpcServerAddress = serverAddress;
|
|
145
|
+
try {
|
|
146
|
+
const splittedData = serverAddress.split(":");
|
|
147
|
+
serverPort = splittedData[1];
|
|
148
|
+
shim.serverPort = parseInt(serverPort);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
const result = original.apply(this, arguments);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Instruments the grpc-js server by intercepting the moment when
|
|
161
|
+
* server methods are registered from the method implementations
|
|
162
|
+
* provided to grpc-js. This handles all four types of server
|
|
163
|
+
* invocations: unary, client-streaming, server-streaming, and
|
|
164
|
+
* bidirectional streaming.
|
|
165
|
+
*
|
|
166
|
+
* @param {object} shim the web shim to instrument with
|
|
167
|
+
* @param {Function} original the original function
|
|
168
|
+
* @returns {Function} the instrumented function
|
|
169
|
+
*/
|
|
170
|
+
function wrapRegister(shim, original) {
|
|
171
|
+
return function wrappedRegister() {
|
|
172
|
+
wrapHandler(shim, arguments, '1');
|
|
173
|
+
const result = original.apply(this, arguments);
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Wrapper to hook handler function
|
|
181
|
+
* @param {*} shim
|
|
182
|
+
* @param {*} mod
|
|
183
|
+
* @param {*} method
|
|
184
|
+
*/
|
|
185
|
+
function wrapHandler(shim, mod, method) {
|
|
186
|
+
shim.wrap(mod, method, function wrapHandler(shim, fn) {
|
|
187
|
+
return function wrappedHandler() {
|
|
188
|
+
const type = this.type;
|
|
189
|
+
const path = this.path;
|
|
190
|
+
wrapCallback(shim, arguments, '1');
|
|
191
|
+
addRequestData(shim, type, path, arguments[0]);
|
|
192
|
+
const result = fn.apply(this, arguments);
|
|
193
|
+
return result;
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Wrapper to hook callback response
|
|
201
|
+
* @param {*} shim
|
|
202
|
+
* @param {*} mod
|
|
203
|
+
* @param {*} method
|
|
204
|
+
*/
|
|
205
|
+
function wrapCallback(shim, mod, method) {
|
|
206
|
+
shim.wrap(mod, method, function wrapCallback(shim, fn) {
|
|
207
|
+
return function wrappedCallback() {
|
|
208
|
+
const response = arguments[1];
|
|
209
|
+
const result = fn.apply(this, arguments);
|
|
210
|
+
return result;
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Utility function to add grpc request data in request map
|
|
218
|
+
* @param {*} shim
|
|
219
|
+
* @param {*} type
|
|
220
|
+
* @param {*} path
|
|
221
|
+
* @param {*} args
|
|
222
|
+
*/
|
|
223
|
+
function addRequestData(shim, type, path, args) {
|
|
224
|
+
const data = Object.assign({});
|
|
225
|
+
try {
|
|
226
|
+
const segment = shim.getActiveSegment();
|
|
227
|
+
if (segment && segment.transaction) {
|
|
228
|
+
data.method = path;
|
|
229
|
+
data.url = path;
|
|
230
|
+
data.uri = path;
|
|
231
|
+
data.headers = args.metadata.getMap();
|
|
232
|
+
if (data.headers && data.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
233
|
+
let bufferObj = Buffer.from(data.headers[NR_CSEC_FUZZ_REQUEST_ID], "base64");
|
|
234
|
+
let decodedString = bufferObj.toString("utf8");
|
|
235
|
+
data.headers[NR_CSEC_FUZZ_REQUEST_ID] = decodedString;
|
|
236
|
+
}
|
|
237
|
+
data.type = type;
|
|
238
|
+
data.protocol = 'grpc';
|
|
239
|
+
data.isGRPC = true;
|
|
240
|
+
data.body = JSON.stringify(args.request);
|
|
241
|
+
data.serverPort = shim.serverPort;
|
|
242
|
+
const transactionId = segment.transaction.id;
|
|
243
|
+
requestManager.setRequest(transactionId, data);
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
logger.error("Error occured while extracting grpc request", error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Utilty to set grpc util Maps
|
|
252
|
+
* @param {*} serviceObject
|
|
253
|
+
*/
|
|
254
|
+
function serviceUtil(serviceObject) {
|
|
255
|
+
let serviceValues = Object.values(serviceObject)[0];
|
|
256
|
+
for (const [key, value] of Object.entries(serviceValues)) {
|
|
257
|
+
let servObj = {
|
|
258
|
+
'serviceName': serviceObject.serviceName,
|
|
259
|
+
'originalName': value.originalName,
|
|
260
|
+
}
|
|
261
|
+
grpcutils.setMethod(value.path, servObj);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function iterateNestedObjectForServiceLookup(result, serviceObject) {
|
|
266
|
+
try {
|
|
267
|
+
for (const key in result) {
|
|
268
|
+
if (typeof result[key] === 'object' && result[key] !== 'null') {
|
|
269
|
+
iterateNestedObjectForServiceLookup(result[key], serviceObject); // Recurse into nested object
|
|
270
|
+
} else {
|
|
271
|
+
let value = result[key];
|
|
272
|
+
if (value && value.serviceName) {
|
|
273
|
+
if (!value.type && value.serviceName) {
|
|
274
|
+
grpcutils.setService(value.serviceName, serviceObject);
|
|
275
|
+
serviceUtil(value);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
logger.error("Error while getting service definition", error);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = initialize
|
|
7
|
+
const requestManager = require('../../core/request-manager');
|
|
8
|
+
const secUtils = require("../../core/sec-utils");
|
|
9
|
+
const API = require("../../../nr-security-api");
|
|
10
|
+
const lodash = require('lodash');
|
|
11
|
+
const securityMetaData = require('../../core/security-metadata');
|
|
12
|
+
const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
|
|
13
|
+
const { NR_CSEC_FUZZ_REQUEST_ID, QUESTION_MARK, EMPTY_STRING, COLON, SLASH } = require('../../core/constants');
|
|
14
|
+
const NRCSECTRACINGDATA = 'NR-CSEC-TRACING-DATA';
|
|
15
|
+
const DOUBLE_SLASH = '//';
|
|
16
|
+
const logger = API.getLogger();
|
|
17
|
+
const url = require('url');
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Entry point undici module instrumentation.
|
|
22
|
+
* @param {*} shim
|
|
23
|
+
* @param {*} mod
|
|
24
|
+
* @param {*} moduleName
|
|
25
|
+
*/
|
|
26
|
+
function initialize(shim, mod, moduleName) {
|
|
27
|
+
logger.info('Instrumenting ' + moduleName);
|
|
28
|
+
fetchHook(shim, mod, "fetch");
|
|
29
|
+
let util = shim.require('./lib/core/util');
|
|
30
|
+
parseOriginHook(shim, util, "parseOrigin")
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Wrapper for util.parseOrigin
|
|
35
|
+
* @param {*} shim
|
|
36
|
+
* @param {*} mod
|
|
37
|
+
* @param {*} methodName
|
|
38
|
+
*/
|
|
39
|
+
function parseOriginHook(shim, mod, methodName) {
|
|
40
|
+
shim.wrap(mod, methodName, function ClientWrapper(shim, fn) {
|
|
41
|
+
logger.debug(`Instrumenting ${methodName}`);
|
|
42
|
+
if (!shim.isFunction(fn)) {
|
|
43
|
+
return fn
|
|
44
|
+
}
|
|
45
|
+
return function wrapper() {
|
|
46
|
+
const request = requestManager.getRequest(shim);
|
|
47
|
+
try {
|
|
48
|
+
if (typeof arguments[0] === 'string') {
|
|
49
|
+
let URLObject = new URL(arguments[0]);
|
|
50
|
+
const data = {};
|
|
51
|
+
data.headers = {};
|
|
52
|
+
data.path = URLObject.pathname ? URLObject.pathname : EMPTY_STRING;
|
|
53
|
+
data.protocol = URLObject.protocol ? URLObject.protocol : 'http:';
|
|
54
|
+
data.headers.host = URLObject.host ? URLObject.host : URLObject.hostname;
|
|
55
|
+
shim.interceptedArgs = data;
|
|
56
|
+
if (request) {
|
|
57
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
58
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, data, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
|
|
59
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
60
|
+
API.sendEvent(this.secEvent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
const result = fn.apply(this, arguments);
|
|
67
|
+
if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
68
|
+
API.generateExitEvent(this.secEvent);
|
|
69
|
+
delete this.secEvent
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Wrapper to hook undici.fetch()
|
|
78
|
+
* @param {*} shim
|
|
79
|
+
* @param {*} mod
|
|
80
|
+
* @param {*} methodName
|
|
81
|
+
*/
|
|
82
|
+
function fetchHook(shim, mod, methodName) {
|
|
83
|
+
shim.wrap(mod, methodName, function makeWrapper(shim, fn) {
|
|
84
|
+
logger.debug(`Instrumenting ${methodName}`);
|
|
85
|
+
if (!shim.isFunction(fn)) {
|
|
86
|
+
return fn
|
|
87
|
+
}
|
|
88
|
+
return function wrapper() {
|
|
89
|
+
const request = requestManager.getRequest(shim);
|
|
90
|
+
try {
|
|
91
|
+
const completeURL = arguments[0];
|
|
92
|
+
const parsedUrl = url.parse(completeURL);
|
|
93
|
+
const data = {};
|
|
94
|
+
data.headers = {};
|
|
95
|
+
data.method = arguments[0].method ? arguments[0].method : EMPTY_STRING;
|
|
96
|
+
data.path = parsedUrl.pathname ? parsedUrl.pathname : parsedUrl.path;
|
|
97
|
+
data.protocol = parsedUrl.protocol;
|
|
98
|
+
data.headers.host = parsedUrl.host ? parsedUrl.host : parsedUrl.hostname;
|
|
99
|
+
data.port = parsedUrl.port ? parsedUrl.port : parsedUrl.port;
|
|
100
|
+
|
|
101
|
+
shim.interceptedArgs = data;
|
|
102
|
+
if (request && !lodash.isEmpty(data.headers.host) && !lodash.isEmpty(data.path)) {
|
|
103
|
+
const appUUID = API.getSecAgent().applicationInfo.applicationUUID;
|
|
104
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
105
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, data, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HTTP_REQUEST, EVENT_CATEGORY.HTTP)
|
|
106
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
107
|
+
API.sendEvent(this.secEvent);
|
|
108
|
+
|
|
109
|
+
let traceHeader = EMPTY_STRING;
|
|
110
|
+
if (request.headers['nr-csec-tracing-data']) {
|
|
111
|
+
traceHeader = request.headers['nr-csec-tracing-data'];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (arguments[1] instanceof Object) {
|
|
115
|
+
if (arguments[1] && arguments[1].headers && request.headers) {
|
|
116
|
+
arguments[1].headers[NRCSECTRACINGDATA] = traceHeader + appUUID + '/' + this.secEvent.apiId + '/' + this.secEvent.id + ';';
|
|
117
|
+
} else {
|
|
118
|
+
arguments[1].headers = {};
|
|
119
|
+
arguments[1].headers[NRCSECTRACINGDATA] = traceHeader + appUUID + '/' + this.secEvent.apiId + '/' + this.secEvent.id + ';';
|
|
120
|
+
}
|
|
121
|
+
if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID] && arguments[1].headers) {
|
|
122
|
+
arguments[1].headers[NR_CSEC_FUZZ_REQUEST_ID] = request.headers[NR_CSEC_FUZZ_REQUEST_ID];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.debug("Error while extracting undici fetch args:", error);
|
|
128
|
+
}
|
|
129
|
+
const result = fn.apply(this, arguments);
|
|
130
|
+
|
|
131
|
+
if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
132
|
+
API.generateExitEvent(this.secEvent);
|
|
133
|
+
delete this.secEvent
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
@@ -190,9 +190,65 @@ newrelic.instrumentWebframework({
|
|
|
190
190
|
}
|
|
191
191
|
})
|
|
192
192
|
|
|
193
|
+
newrelic.instrumentWebframework({
|
|
194
|
+
moduleName: 'undici',
|
|
195
|
+
isEsm: true,
|
|
196
|
+
onRequire: require('./hooks/undici/nr-undici'),
|
|
197
|
+
onError: function intrumentErrorHandler(err) {
|
|
198
|
+
logger.error(err.message, err.stack)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
193
202
|
|
|
203
|
+
newrelic.instrument({
|
|
204
|
+
type: 'conglomerate',
|
|
205
|
+
moduleName: '@grpc/grpc-js/build/src/server',
|
|
206
|
+
isEsm: true,
|
|
207
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapServer,
|
|
208
|
+
onError: function intrumentErrorHandler(err) {
|
|
209
|
+
logger.error(err.message, err.stack)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
194
212
|
|
|
213
|
+
newrelic.instrument({
|
|
214
|
+
type: 'conglomerate',
|
|
215
|
+
moduleName: '@grpc/grpc-js/build/src/make-client',
|
|
216
|
+
isEsm: true,
|
|
217
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapMakeClient,
|
|
218
|
+
onError: function intrumentErrorHandler(err) {
|
|
219
|
+
logger.error(err.message, err.stack)
|
|
220
|
+
}
|
|
221
|
+
})
|
|
195
222
|
|
|
223
|
+
newrelic.instrument({
|
|
224
|
+
type: 'conglomerate',
|
|
225
|
+
moduleName: '@grpc/grpc-js/build/src/resolving-call',
|
|
226
|
+
isEsm: true,
|
|
227
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartResolve,
|
|
228
|
+
onError: function intrumentErrorHandler(err) {
|
|
229
|
+
logger.error(err.message, err.stack)
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
newrelic.instrument({
|
|
234
|
+
type: 'conglomerate',
|
|
235
|
+
moduleName: '@grpc/grpc-js/build/src/call-stream',
|
|
236
|
+
isEsm: true,
|
|
237
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartCall,
|
|
238
|
+
onError: function intrumentErrorHandler(err) {
|
|
239
|
+
logger.error(err.message, err.stack)
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
newrelic.instrument({
|
|
244
|
+
type: 'conglomerate',
|
|
245
|
+
moduleName: '@grpc/grpc-js',
|
|
246
|
+
isEsm: true,
|
|
247
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').initialize,
|
|
248
|
+
onError: function intrumentErrorHandler(err) {
|
|
249
|
+
logger.error(err.message, err.stack)
|
|
250
|
+
}
|
|
251
|
+
})
|
|
196
252
|
|
|
197
253
|
|
|
198
254
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: New Relic Software License v1.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { BasicInfo } = require('./event');
|
|
8
|
+
const applicationInfo = require('./applicationinfo').getInstance();
|
|
9
|
+
/**
|
|
10
|
+
* Function Constructor to create object of FuzzFail event
|
|
11
|
+
* @param {*} fuzzHeader
|
|
12
|
+
*/
|
|
13
|
+
function APIEndPoint (apiEndpoints) {
|
|
14
|
+
BasicInfo.call(this);
|
|
15
|
+
this.jsonVersion = applicationInfo.jsonVersion;
|
|
16
|
+
this.jsonName = 'sec-application-url-mapping';
|
|
17
|
+
this.eventType = "sec-application-url-mapping"
|
|
18
|
+
this.applicationUUID = applicationInfo.applicationUUID;
|
|
19
|
+
this.groupName = applicationInfo.groupName;
|
|
20
|
+
this.mappings = apiEndpoints;
|
|
21
|
+
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
APIEndPoint.prototype.constructor = APIEndPoint;
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
APIEndPoint
|
|
29
|
+
};
|
|
@@ -97,11 +97,11 @@ function runtimeSupportsAsyncFunctionGenerators() {
|
|
|
97
97
|
function createPathIfNotExist(dir) {
|
|
98
98
|
try {
|
|
99
99
|
if (!fs.existsSync(dir)) {
|
|
100
|
-
fs.mkdirSync(dir,
|
|
100
|
+
fs.mkdirSync(dir, 770, { recursive: true });
|
|
101
101
|
logger.info(dir + ' Created');
|
|
102
|
-
fs.chmodSync(dir,
|
|
102
|
+
fs.chmodSync(dir, 0o770);
|
|
103
103
|
} else {
|
|
104
|
-
fs.chmodSync(dir,
|
|
104
|
+
fs.chmodSync(dir, 0o770);
|
|
105
105
|
logger.debug(dir + ' Already Exists');
|
|
106
106
|
}
|
|
107
107
|
} catch (error) {
|
|
@@ -139,7 +139,7 @@ function createDirectories() {
|
|
|
139
139
|
createPathIfNotExist(`${CSEC_HOME}${SLASH}nr-security-home${SLASH}tmp${SLASH}language-agent${SLASH}${getUUID()}`);
|
|
140
140
|
createPathIfNotExist(`${CSEC_HOME}${SLASH}nr-security-home${SLASH}tmp${SLASH}language-agent${SLASH}${getUUID()}${SLASH}ds-tmp`);
|
|
141
141
|
try {
|
|
142
|
-
fs.chmodSync(`${CSEC_HOME}${SLASH}nr-security-home`,
|
|
142
|
+
fs.chmodSync(`${CSEC_HOME}${SLASH}nr-security-home`, 0o770);
|
|
143
143
|
} catch (error) {
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: New Relic Pre-Release
|
|
4
|
+
*/
|
|
5
|
+
const logger = require('../logging').getLogger();
|
|
6
|
+
const grpcutils = require('../../../../instrumentation-security/core/grpc-utils');
|
|
7
|
+
const clientMap = new Map();
|
|
8
|
+
let grpc;
|
|
9
|
+
const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/sec-agent-constants');
|
|
10
|
+
try {
|
|
11
|
+
grpc = require("@grpc/grpc-js");
|
|
12
|
+
} catch (error) {
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Utility to get client object
|
|
18
|
+
* @param {*} config
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
function getGPRCClient(config) {
|
|
22
|
+
try {
|
|
23
|
+
const serviceName = grpcutils.getMethod(config.method).serviceName;
|
|
24
|
+
|
|
25
|
+
if (clientMap.has(serviceName)) {
|
|
26
|
+
return clientMap.get(serviceName);
|
|
27
|
+
}
|
|
28
|
+
const serviceObject = grpcutils.getService(serviceName);
|
|
29
|
+
const definition = grpc.loadPackageDefinition(serviceObject);
|
|
30
|
+
let service = getNestedValue(definition, serviceName)
|
|
31
|
+
if (service) {
|
|
32
|
+
logger.info("GRPC client service extracted successfully");
|
|
33
|
+
}
|
|
34
|
+
const client = new service(
|
|
35
|
+
"localhost:" + config.serverPort,
|
|
36
|
+
grpc.credentials.createInsecure()
|
|
37
|
+
);
|
|
38
|
+
if (!clientMap.has(serviceName)) {
|
|
39
|
+
clientMap.set(serviceName, client);
|
|
40
|
+
}
|
|
41
|
+
if(client){
|
|
42
|
+
logger.info("GRPC IAST client created successfully");
|
|
43
|
+
}
|
|
44
|
+
return client;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger.error("error while creating client:", error);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Utility to fire unary requests
|
|
53
|
+
* @param {*} client
|
|
54
|
+
* @param {*} config
|
|
55
|
+
*/
|
|
56
|
+
function fireUnaryRequest(client, config) {
|
|
57
|
+
try {
|
|
58
|
+
const methodObj = grpcutils.getMethod(config.requestURI);
|
|
59
|
+
if (methodObj && methodObj.originalName) {
|
|
60
|
+
let method = methodObj.originalName;
|
|
61
|
+
let metadata = new grpc.Metadata();
|
|
62
|
+
let bufferObj = Buffer.from(config.headers[NR_CSEC_FUZZ_REQUEST_ID], "utf8");
|
|
63
|
+
let base64String = bufferObj.toString("base64");
|
|
64
|
+
metadata.add(NR_CSEC_FUZZ_REQUEST_ID, base64String);
|
|
65
|
+
client[method](JSON.parse(config.data), metadata, (err, res) => {
|
|
66
|
+
if (err) {
|
|
67
|
+
logger.debug("Error occured:", err);
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logger.info("Fuzz success: ", JSON.stringify(config));
|
|
72
|
+
return res;
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
logger.debug("Not Found:", config.url);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.debug("Error while firing Unary request:", error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* fire gRPC request based on provided config
|
|
85
|
+
* @param {*} config
|
|
86
|
+
* @returns
|
|
87
|
+
*/
|
|
88
|
+
function fireRequest(config) {
|
|
89
|
+
let client = getGPRCClient(config);
|
|
90
|
+
fireUnaryRequest(client, config)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
function getNestedValue(obj, targetKey) {
|
|
95
|
+
for (const key in obj) {
|
|
96
|
+
if (key === targetKey) {
|
|
97
|
+
return obj[key];
|
|
98
|
+
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
99
|
+
const result = getNestedValue(obj[key], targetKey);
|
|
100
|
+
if (result !== undefined) {
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return undefined; // Key not found
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
fireRequest
|
|
111
|
+
};
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const stringify = require('fast-safe-stringify');
|
|
8
8
|
|
|
9
9
|
const restClient = require('../../restclient');
|
|
10
|
+
const grpcClient = require('../../grpcClient');
|
|
10
11
|
const { Agent } = require('../../../agent');
|
|
11
12
|
const { FuzzFailEvent } = require('../../../FuzzFailEvent');
|
|
12
13
|
const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP, SEVERE } = require('../../../sec-agent-constants');
|
|
@@ -20,9 +21,6 @@ const LogMessage = require('../../../LogMessage');
|
|
|
20
21
|
require('dns').setDefaultResultOrder('ipv4first')
|
|
21
22
|
const PolicyManager = require('../../../Policy');
|
|
22
23
|
|
|
23
|
-
const {
|
|
24
|
-
IS_LAMBDA_ENV,
|
|
25
|
-
AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER } = require('../../../sec-agent-constants');
|
|
26
24
|
const statusUtils = require('../../../statusUtils');
|
|
27
25
|
|
|
28
26
|
const find = `${SLASH}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
|
|
@@ -125,13 +123,16 @@ function handler(json) {
|
|
|
125
123
|
function handleFuzzRequest(fuzzDetails) {
|
|
126
124
|
const { fuzzRequest } = fuzzDetails;
|
|
127
125
|
try {
|
|
128
|
-
if (
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
if (fuzzRequest.protocol == 'grpc') {
|
|
127
|
+
const config = parseGRPCRequestToFuzz(fuzzRequest);
|
|
128
|
+
config.headers['nr-csec-parent-id'] = fuzzRequest.id;
|
|
129
|
+
IASTUtil.completedRequestsMapInit(fuzzRequest.id);
|
|
130
|
+
grpcClient.fireRequest(config);
|
|
131
|
+
if (fuzzRequest.headers && fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
132
|
+
logScannedApiId(fuzzRequest.headers[NR_CSEC_FUZZ_REQUEST_ID], fuzzRequest.requestURI)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
IASTUtil.removePendingRequestId(fuzzRequest.id);
|
|
135
136
|
|
|
136
137
|
} else {
|
|
137
138
|
const config = parseAxiosHttpRequestToFuzz(fuzzRequest);
|
|
@@ -153,7 +154,6 @@ function handleFuzzRequest(fuzzDetails) {
|
|
|
153
154
|
|
|
154
155
|
|
|
155
156
|
logger.info('Firing http request:: URL: ' + config.url);
|
|
156
|
-
|
|
157
157
|
const response = restClient.fireRequest(config);
|
|
158
158
|
handleFuzzResponse(response, fuzzDetails);
|
|
159
159
|
}
|
|
@@ -210,6 +210,26 @@ function parseAxiosHttpRequestToFuzz(requestObject) {
|
|
|
210
210
|
};
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Parses the passed fuzz request object to
|
|
215
|
+
* Axios config.
|
|
216
|
+
*
|
|
217
|
+
* @param {JSON} requestObject
|
|
218
|
+
*/
|
|
219
|
+
function parseGRPCRequestToFuzz(requestObject) {
|
|
220
|
+
let serverName = requestObject.serverName ? requestObject.serverName : LOCALHOST;
|
|
221
|
+
let host = serverName + COLON + requestObject.serverPort
|
|
222
|
+
return {
|
|
223
|
+
url: requestObject.protocol + COLON_SLASH_SLASH + host + requestObject.url,
|
|
224
|
+
requestURI: requestObject.requestURI,
|
|
225
|
+
protocol: requestObject.protocol,
|
|
226
|
+
serverPort: requestObject.serverPort,
|
|
227
|
+
method: requestObject.method,
|
|
228
|
+
data: requestObject.body,
|
|
229
|
+
headers: requestObject.headers,
|
|
230
|
+
timeout: 5000,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
213
233
|
|
|
214
234
|
|
|
215
235
|
/**
|
|
@@ -31,11 +31,11 @@ const CSEC_ROOT = `${CSEC_HOME}${sep}nr-security-home${sep}`;
|
|
|
31
31
|
function createLogPathIfNotExist(dir) {
|
|
32
32
|
try {
|
|
33
33
|
if (!fs.existsSync(dir)) {
|
|
34
|
-
fs.mkdirSync(dir,
|
|
35
|
-
fs.chmodSync(dir,
|
|
34
|
+
fs.mkdirSync(dir, 770, { recursive: true });
|
|
35
|
+
fs.chmodSync(dir, 0o770);
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
|
-
fs.chmodSync(dir,
|
|
38
|
+
fs.chmodSync(dir, 0o770);
|
|
39
39
|
}
|
|
40
40
|
} catch (error) {
|
|
41
41
|
}
|
|
@@ -168,8 +168,8 @@ function getLogger(applicationUUID = process.env.applicationUUID) {
|
|
|
168
168
|
logger = log4js.getLogger('NR_CSEC');
|
|
169
169
|
const initLogger = getInitLogger();
|
|
170
170
|
try {
|
|
171
|
-
fs.chmodSync(`${logFileName}`,
|
|
172
|
-
fs.chmodSync(`${initLogFileName}`,
|
|
171
|
+
fs.chmodSync(`${logFileName}`, 0o660);
|
|
172
|
+
fs.chmodSync(`${initLogFileName}`, 0o660);
|
|
173
173
|
} catch (error) {
|
|
174
174
|
}
|
|
175
175
|
initLogger.info("[STEP-2] => Generating unique identifier:", applicationUUID)
|
|
@@ -93,7 +93,7 @@ function getFormattedData() {
|
|
|
93
93
|
function writeSnapshot() {
|
|
94
94
|
const snapshot = getFormattedData();
|
|
95
95
|
commonUtils.removeOlderSnapshots();
|
|
96
|
-
fs.writeFile(statusFile, snapshot, { mode:
|
|
96
|
+
fs.writeFile(statusFile, snapshot, { mode: 0o660 }, function (err) {
|
|
97
97
|
if (err) {
|
|
98
98
|
logger.debug(err.message);
|
|
99
99
|
const LogMessage = require('./LogMessage');
|
|
@@ -101,7 +101,7 @@ function writeSnapshot() {
|
|
|
101
101
|
commonUtils.addLogEventtoBuffer(logMessage);
|
|
102
102
|
} else {
|
|
103
103
|
logger.info('Snapshot updated to file: %s', statusFile);
|
|
104
|
-
fs.chmod(statusFile,
|
|
104
|
+
fs.chmod(statusFile, 0o660, (err) => {
|
|
105
105
|
if (err) {
|
|
106
106
|
addErrortoBuffer(err);
|
|
107
107
|
}
|
|
@@ -70,6 +70,15 @@ const initialize = function () {
|
|
|
70
70
|
setTimeout(() => {
|
|
71
71
|
setInterval(sendHC, HC_INTERVAL_MS);
|
|
72
72
|
sendHC();
|
|
73
|
+
try {
|
|
74
|
+
let apiEndpoints = require('../../../instrumentation-security/core/route-manager').getAllAPIEndPoints();
|
|
75
|
+
let data = require('./apiEndpoints').APIEndPoint(apiEndpoints);
|
|
76
|
+
logger.debug("All API end points of the application is:",JSON.stringify(data));
|
|
77
|
+
getDispatcherAndSendEvent(data);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.debug("Error while processing API endpoints",error);
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
try {
|
|
74
83
|
const framework = NRAgent.environment.get('Framework')[0];
|
|
75
84
|
const dispatcher = NRAgent.environment.get('Dispatcher')[0];
|
|
@@ -78,8 +87,8 @@ const initialize = function () {
|
|
|
78
87
|
} catch (error) {
|
|
79
88
|
logger.debug("Error while extracting data from APM environment",error);
|
|
80
89
|
}
|
|
90
|
+
}, 20000);
|
|
81
91
|
|
|
82
|
-
}, 30000);
|
|
83
92
|
|
|
84
93
|
setInterval(commonUtils.logRollOver, 30000);
|
|
85
94
|
initLogger.info("[STEP-5] => Security Agent components started");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/security-agent",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "New Relic Security Agent for Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"jsonVersion": "1.1.1",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"url": "https://github.com/newrelic/csec-node-agent.git"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"axios": "^1.6.
|
|
38
|
+
"axios": "^1.6.8",
|
|
39
39
|
"check-disk-space": "^3.4.0",
|
|
40
40
|
"content-type": "^1.0.5",
|
|
41
41
|
"fast-safe-stringify": "^2.1.1",
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
"ws": "^8.14.2"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
+
"@grpc/grpc-js": "^1.9.5",
|
|
62
|
+
"@grpc/proto-loader": "^0.7.10",
|
|
61
63
|
"@hapi/hapi": "^21.3.0",
|
|
62
64
|
"@koa/router": "^12.0.0",
|
|
63
65
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
@@ -84,7 +86,9 @@
|
|
|
84
86
|
"request": "^2.88.2",
|
|
85
87
|
"restify": "^11.1.0",
|
|
86
88
|
"sinon": "^15.0.0",
|
|
89
|
+
"stream": "^0.0.2",
|
|
87
90
|
"tap": "^16.3.2",
|
|
91
|
+
"undici": "^5.28.0",
|
|
88
92
|
"xmldom": "^0.6.0",
|
|
89
93
|
"xpath": "^0.0.32",
|
|
90
94
|
"xpath.js": "^1.1.0"
|