@newrelic/security-agent 1.2.0 → 1.3.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 +12 -0
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +210 -209
- package/lib/instrumentation-security/core/grpc-utils.js +53 -0
- package/lib/instrumentation-security/core/security-metadata.js +12 -2
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +4 -4
- package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +301 -0
- package/lib/instrumentation-security/index.js +32 -3
- package/lib/nr-security-agent/index.js +7 -1
- package/lib/nr-security-agent/lib/core/commonUtils.js +2 -2
- package/lib/nr-security-agent/lib/core/connections/grpcClient.js +112 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +3 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +32 -12
- package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +1 -1
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +9 -4
- package/lib/nr-security-agent/lib/core/sec-util.js +1 -0
- package/lib/nr-security-agent/lib/core/statusUtils.js +1 -1
- package/lib/nr-security-agent/lib/core/websocket-client.js +1 -1
- package/lib/nr-security-agent/lib/core/xss-utils.js +11 -5
- package/package.json +2 -1
|
@@ -0,0 +1,301 @@
|
|
|
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
|
+
module.exports.initialize = function initialize(shim, grpc, moduleName) {
|
|
19
|
+
logger.info('Instrumenting grpc', moduleName);
|
|
20
|
+
const grpcVersion = shim.require('./package.json').version;
|
|
21
|
+
logger.debug("grpc version:", grpcVersion);
|
|
22
|
+
|
|
23
|
+
if (semver.lt(grpcVersion, '1.4.0')) {
|
|
24
|
+
logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (semver.gte(grpcVersion, '1.8.0')) {
|
|
29
|
+
const resolvingCall = shim.require('./build/src/resolving-call')
|
|
30
|
+
shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
31
|
+
} else {
|
|
32
|
+
const callStream = shim.require('./build/src/call-stream')
|
|
33
|
+
shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const server = shim.require('./build/src/server')
|
|
37
|
+
shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
|
|
38
|
+
shim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
39
|
+
|
|
40
|
+
//get all service
|
|
41
|
+
shim.wrap(server.Server.prototype, 'addService', wrapAddService);
|
|
42
|
+
|
|
43
|
+
//load package definition
|
|
44
|
+
const makeClient = shim.require('./build/src/make-client');
|
|
45
|
+
shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports.wrapStartResolve = function wrappedStartResolve(shim, resolvingCall) {
|
|
50
|
+
shim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports.wrapStartCall = function wrappedClient(shim, callStream) {
|
|
54
|
+
shim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports.wrapServer = function wrappedServer(shim, server) {
|
|
58
|
+
const grpcVersion = shim.require('./package.json').version
|
|
59
|
+
logger.info("Instrumenting @grpc/grpc-js", grpcVersion)
|
|
60
|
+
if (semver.lt(grpcVersion, '1.4.0')) {
|
|
61
|
+
logger.warn('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
shim.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
66
|
+
shim.wrap(server.Server.prototype, 'bindAsync', bindAsyncWrapper)
|
|
67
|
+
//get all service
|
|
68
|
+
shim.wrap(server.Server.prototype, 'addService', wrapAddService);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports.wrapMakeClient = function wrapMakeClient(shim, makeClient) {
|
|
72
|
+
shim.wrap(makeClient, 'loadPackageDefinition', wrapLoadPackageDefinition);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Instruments grpc-js client by intercepting the function that
|
|
78
|
+
* initiates client requests. This handles all four types of client
|
|
79
|
+
* invocations: unary, client-streaming, server-streaming, and
|
|
80
|
+
* bidirectional streaming.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} shim the generic shim to instrument with
|
|
83
|
+
* @param {Function} original the original function
|
|
84
|
+
* @returns {Function} the instrumented function
|
|
85
|
+
*/
|
|
86
|
+
function wrapStart(shim, original) {
|
|
87
|
+
if (!shim.isFunction(original)) {
|
|
88
|
+
return original
|
|
89
|
+
}
|
|
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
|
+
if (!shim.isFunction(original)) {
|
|
124
|
+
return original
|
|
125
|
+
}
|
|
126
|
+
return function wrappedbindAsync() {
|
|
127
|
+
const result = original.apply(this, arguments);
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function wrapLoadPackageDefinition(shim, original) {
|
|
133
|
+
if (!shim.isFunction(original)) {
|
|
134
|
+
return original
|
|
135
|
+
}
|
|
136
|
+
return function wrappedbindAsync() {
|
|
137
|
+
let serviceObject = arguments[0];
|
|
138
|
+
const result = original.apply(this, arguments);
|
|
139
|
+
iterateNestedObjectForServiceLookup(result, serviceObject);
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
function bindAsyncWrapper(shim, original) {
|
|
148
|
+
if (!shim.isFunction(original)) {
|
|
149
|
+
return original
|
|
150
|
+
}
|
|
151
|
+
return function wrappedbindAsync() {
|
|
152
|
+
serverAddress = arguments[0];
|
|
153
|
+
shim.grpcServerAddress = serverAddress;
|
|
154
|
+
try {
|
|
155
|
+
const splittedData = serverAddress.split(":");
|
|
156
|
+
serverPort = splittedData[1];
|
|
157
|
+
shim.serverPort = parseInt(serverPort);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
const result = original.apply(this, arguments);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Instruments the grpc-js server by intercepting the moment when
|
|
170
|
+
* server methods are registered from the method implementations
|
|
171
|
+
* provided to grpc-js. This handles all four types of server
|
|
172
|
+
* invocations: unary, client-streaming, server-streaming, and
|
|
173
|
+
* bidirectional streaming.
|
|
174
|
+
*
|
|
175
|
+
* @param {object} shim the web shim to instrument with
|
|
176
|
+
* @param {Function} original the original function
|
|
177
|
+
* @returns {Function} the instrumented function
|
|
178
|
+
*/
|
|
179
|
+
function wrapRegister(shim, original) {
|
|
180
|
+
if (!shim.isFunction(original)) {
|
|
181
|
+
return original
|
|
182
|
+
}
|
|
183
|
+
return function wrappedRegister() {
|
|
184
|
+
wrapHandler(shim, arguments, '1');
|
|
185
|
+
const result = original.apply(this, arguments);
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Wrapper to hook handler function
|
|
193
|
+
* @param {*} shim
|
|
194
|
+
* @param {*} mod
|
|
195
|
+
* @param {*} method
|
|
196
|
+
*/
|
|
197
|
+
function wrapHandler(shim, mod, method) {
|
|
198
|
+
shim.wrap(mod, method, function wrapHandler(shim, fn) {
|
|
199
|
+
if (!shim.isFunction(fn)) {
|
|
200
|
+
return fn
|
|
201
|
+
}
|
|
202
|
+
return function wrappedHandler() {
|
|
203
|
+
const type = this.type;
|
|
204
|
+
const path = this.path;
|
|
205
|
+
wrapCallback(shim, arguments, '1');
|
|
206
|
+
addRequestData(shim, type, path, arguments[0]);
|
|
207
|
+
const result = fn.apply(this, arguments);
|
|
208
|
+
return result;
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Wrapper to hook callback response
|
|
216
|
+
* @param {*} shim
|
|
217
|
+
* @param {*} mod
|
|
218
|
+
* @param {*} method
|
|
219
|
+
*/
|
|
220
|
+
function wrapCallback(shim, mod, method) {
|
|
221
|
+
shim.wrap(mod, method, function wrapCallback(shim, fn) {
|
|
222
|
+
if (!shim.isFunction(fn)) {
|
|
223
|
+
return fn
|
|
224
|
+
}
|
|
225
|
+
return function wrappedCallback() {
|
|
226
|
+
const response = arguments[1];
|
|
227
|
+
const result = fn.apply(this, arguments);
|
|
228
|
+
return result;
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Utility function to add grpc request data in request map
|
|
236
|
+
* @param {*} shim
|
|
237
|
+
* @param {*} type
|
|
238
|
+
* @param {*} path
|
|
239
|
+
* @param {*} args
|
|
240
|
+
*/
|
|
241
|
+
function addRequestData(shim, type, path, args) {
|
|
242
|
+
const data = Object.assign({});
|
|
243
|
+
try {
|
|
244
|
+
const segment = shim.getActiveSegment();
|
|
245
|
+
if (segment && segment.transaction) {
|
|
246
|
+
data.method = path;
|
|
247
|
+
data.url = path;
|
|
248
|
+
data.uri = path;
|
|
249
|
+
data.headers = args.metadata.getMap();
|
|
250
|
+
if (data.headers && data.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
251
|
+
let bufferObj = Buffer.from(data.headers[NR_CSEC_FUZZ_REQUEST_ID], "base64");
|
|
252
|
+
let decodedString = bufferObj.toString("utf8");
|
|
253
|
+
data.headers[NR_CSEC_FUZZ_REQUEST_ID] = decodedString;
|
|
254
|
+
}
|
|
255
|
+
data.type = type;
|
|
256
|
+
data.protocol = 'grpc';
|
|
257
|
+
data.isGRPC = true;
|
|
258
|
+
data.body = JSON.stringify(args.request);
|
|
259
|
+
data.serverPort = shim.serverPort;
|
|
260
|
+
const transactionId = segment.transaction.id;
|
|
261
|
+
requestManager.setRequest(transactionId, data);
|
|
262
|
+
}
|
|
263
|
+
} catch (error) {
|
|
264
|
+
logger.error("Error occured while extracting grpc request", error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Utilty to set grpc util Maps
|
|
270
|
+
* @param {*} serviceObject
|
|
271
|
+
*/
|
|
272
|
+
function serviceUtil(serviceObject) {
|
|
273
|
+
let serviceValues = Object.values(serviceObject)[0];
|
|
274
|
+
for (const [key, value] of Object.entries(serviceValues)) {
|
|
275
|
+
let servObj = {
|
|
276
|
+
'serviceName': serviceObject.serviceName,
|
|
277
|
+
'originalName': value.originalName,
|
|
278
|
+
}
|
|
279
|
+
grpcutils.setMethod(value.path, servObj);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function iterateNestedObjectForServiceLookup(result, serviceObject) {
|
|
284
|
+
try {
|
|
285
|
+
for (const key in result) {
|
|
286
|
+
if (typeof result[key] === 'object' && result[key] !== 'null') {
|
|
287
|
+
iterateNestedObjectForServiceLookup(result[key], serviceObject); // Recurse into nested object
|
|
288
|
+
} else {
|
|
289
|
+
let value = result[key];
|
|
290
|
+
if (value && value.serviceName) {
|
|
291
|
+
if (!value.type && value.serviceName) {
|
|
292
|
+
grpcutils.setService(value.serviceName, serviceObject);
|
|
293
|
+
serviceUtil(value);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
logger.error("Error while getting service definition", error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -199,10 +199,39 @@ newrelic.instrumentWebframework({
|
|
|
199
199
|
}
|
|
200
200
|
})
|
|
201
201
|
|
|
202
|
+
newrelic.instrument({
|
|
203
|
+
moduleName: '@grpc/grpc-js/build/src/server',
|
|
204
|
+
isEsm: true,
|
|
205
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapServer,
|
|
206
|
+
onError: function intrumentErrorHandler(err) {
|
|
207
|
+
logger.error(err.message, err.stack)
|
|
208
|
+
}
|
|
209
|
+
})
|
|
202
210
|
|
|
211
|
+
newrelic.instrument({
|
|
212
|
+
moduleName: '@grpc/grpc-js/build/src/make-client',
|
|
213
|
+
isEsm: true,
|
|
214
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapMakeClient,
|
|
215
|
+
onError: function intrumentErrorHandler(err) {
|
|
216
|
+
logger.error(err.message, err.stack)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
203
219
|
|
|
220
|
+
newrelic.instrument({
|
|
221
|
+
moduleName: '@grpc/grpc-js/build/src/resolving-call',
|
|
222
|
+
isEsm: true,
|
|
223
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartResolve,
|
|
224
|
+
onError: function intrumentErrorHandler(err) {
|
|
225
|
+
logger.error(err.message, err.stack)
|
|
226
|
+
}
|
|
227
|
+
})
|
|
204
228
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
229
|
+
newrelic.instrument({
|
|
230
|
+
moduleName: '@grpc/grpc-js/build/src/call-stream',
|
|
231
|
+
isEsm: true,
|
|
232
|
+
onRequire: require('./hooks/grpc-js/nr-grpc').wrapStartCall,
|
|
233
|
+
onError: function intrumentErrorHandler(err) {
|
|
234
|
+
logger.error(err.message, err.stack)
|
|
235
|
+
}
|
|
236
|
+
})
|
|
208
237
|
|
|
@@ -16,6 +16,13 @@ const njsAgentConstants = require('./lib/core/sec-agent-constants');
|
|
|
16
16
|
|
|
17
17
|
const NRLogger = API.newrelic.shim.logger.child({ component: 'security-agent' });
|
|
18
18
|
NRLogger.info("Starting New Relic Node.js Security Agent ");
|
|
19
|
+
try {
|
|
20
|
+
if (NRAgent.config.security.enabled){
|
|
21
|
+
require('../instrumentation-security');
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error("Error while applying security instrumentation")
|
|
25
|
+
}
|
|
19
26
|
|
|
20
27
|
const {
|
|
21
28
|
IS_LAMBDA_ENV,
|
|
@@ -169,7 +176,6 @@ function getProcCMDLineSanitized(applicationInfoModule) {
|
|
|
169
176
|
return;
|
|
170
177
|
}
|
|
171
178
|
if (initialize()) {
|
|
172
|
-
require('../instrumentation-security');
|
|
173
179
|
initLogger.info("[STEP-6] => Application instrumentation applied successfully");
|
|
174
180
|
}
|
|
175
181
|
})();
|
|
@@ -107,7 +107,7 @@ function createPathIfNotExist(dir) {
|
|
|
107
107
|
} catch (error) {
|
|
108
108
|
logger.debug(dir + ' Not Created', error);
|
|
109
109
|
const LogMessage = require('./LogMessage');
|
|
110
|
-
const logMessage = new LogMessage.logMessage("
|
|
110
|
+
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating directory', __filename, error);
|
|
111
111
|
addLogEventtoBuffer(logMessage);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -288,7 +288,7 @@ function removeOlderSnapshots() {
|
|
|
288
288
|
} catch (error) {
|
|
289
289
|
logger.debug(error);
|
|
290
290
|
const LogMessage = require('./LogMessage');
|
|
291
|
-
const logMessage = new LogMessage.logMessage("
|
|
291
|
+
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in processing snapshot files', __filename, error);
|
|
292
292
|
addLogEventtoBuffer(logMessage);
|
|
293
293
|
}
|
|
294
294
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Utility to get client object
|
|
13
|
+
* @param {*} config
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
function getGPRCClient(config) {
|
|
17
|
+
let grpc = require("@grpc/grpc-js");
|
|
18
|
+
try {
|
|
19
|
+
const serviceName = grpcutils.getMethod(config.method).serviceName;
|
|
20
|
+
|
|
21
|
+
if (clientMap.has(serviceName)) {
|
|
22
|
+
return clientMap.get(serviceName);
|
|
23
|
+
}
|
|
24
|
+
const serviceObject = grpcutils.getService(serviceName);
|
|
25
|
+
const definition = grpc.loadPackageDefinition(serviceObject);
|
|
26
|
+
let service = getNestedValue(definition, serviceName)
|
|
27
|
+
if (service) {
|
|
28
|
+
logger.info("GRPC client service extracted successfully");
|
|
29
|
+
}
|
|
30
|
+
const client = new service(
|
|
31
|
+
"localhost:" + config.serverPort,
|
|
32
|
+
grpc.credentials.createInsecure()
|
|
33
|
+
);
|
|
34
|
+
if (!clientMap.has(serviceName)) {
|
|
35
|
+
clientMap.set(serviceName, client);
|
|
36
|
+
}
|
|
37
|
+
if(client){
|
|
38
|
+
logger.info("GRPC IAST client created successfully");
|
|
39
|
+
}
|
|
40
|
+
return client;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.error("error while creating client:", error);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Utility to fire unary requests
|
|
49
|
+
* @param {*} client
|
|
50
|
+
* @param {*} config
|
|
51
|
+
*/
|
|
52
|
+
function fireUnaryRequest(client, config) {
|
|
53
|
+
try {
|
|
54
|
+
const methodObj = grpcutils.getMethod(config.requestURI);
|
|
55
|
+
if (methodObj && methodObj.originalName) {
|
|
56
|
+
let method = methodObj.originalName;
|
|
57
|
+
let metadata = new grpc.Metadata();
|
|
58
|
+
let bufferObj = Buffer.from(config.headers[NR_CSEC_FUZZ_REQUEST_ID], "utf8");
|
|
59
|
+
let base64String = bufferObj.toString("base64");
|
|
60
|
+
metadata.add(NR_CSEC_FUZZ_REQUEST_ID, base64String);
|
|
61
|
+
client[method](JSON.parse(config.data), metadata, (err, res) => {
|
|
62
|
+
if (err) {
|
|
63
|
+
logger.debug("Error occured:", err);
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
logger.info("Fuzz success: ", JSON.stringify(config));
|
|
68
|
+
return res;
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
logger.debug("Not Found:", config.url);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
logger.debug("Error while firing Unary request:", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* fire gRPC request based on provided config
|
|
81
|
+
* @param {*} config
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
function fireRequest(config) {
|
|
85
|
+
try {
|
|
86
|
+
grpc = require("@grpc/grpc-js");
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.debug("Unable to import grpc module", error);
|
|
89
|
+
}
|
|
90
|
+
let client = getGPRCClient(config);
|
|
91
|
+
fireUnaryRequest(client, config)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
function getNestedValue(obj, targetKey) {
|
|
96
|
+
for (const key in obj) {
|
|
97
|
+
if (key === targetKey) {
|
|
98
|
+
return obj[key];
|
|
99
|
+
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
100
|
+
const result = getNestedValue(obj[key], targetKey);
|
|
101
|
+
if (result !== undefined) {
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return undefined; // Key not found
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
fireRequest
|
|
112
|
+
};
|
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}}`;
|
|
@@ -53,7 +51,7 @@ function startIASTSchedular() {
|
|
|
53
51
|
if (isNaN(probingInterval)) {
|
|
54
52
|
probingInterval = 5;
|
|
55
53
|
}
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
iastIntervalConst = setInterval(() => {
|
|
58
56
|
let data = IASTUtil.generateIASTDataRequest();
|
|
59
57
|
let currentTime = Date.now();
|
|
@@ -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
|
/**
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js
CHANGED
|
@@ -18,7 +18,7 @@ module.exports.handler = function handler(json) {
|
|
|
18
18
|
|
|
19
19
|
initLogger.info(LOG_MESSAGES.AGENT_INIT_WITH_PROP, JSON.stringify(startupConfig.config));
|
|
20
20
|
initLogger.info('Security Agent is now ACTIVE for', applicationInfoModule.getInstance().applicationUUID);
|
|
21
|
-
const logMessage = new LogMessage.logMessage("
|
|
21
|
+
const logMessage = new LogMessage.logMessage("INFO", `Security Agent is ACTIVE for ${ applicationInfoModule.getInstance().applicationUUID}`, __filename, null);
|
|
22
22
|
require('../../../commonUtils').addLogEventtoBuffer(logMessage);
|
|
23
23
|
|
|
24
24
|
} catch (error) {
|
|
@@ -104,6 +104,7 @@ SecWebSocket.prototype.init = function init() {
|
|
|
104
104
|
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
105
105
|
const authHeaders = require('../../Auth-headers').getInstance();
|
|
106
106
|
initLogger.info(`Connecting to Validator at ${validatorService}`);
|
|
107
|
+
IASTUtil.IASTCleanup();
|
|
107
108
|
let cert = ''
|
|
108
109
|
try {
|
|
109
110
|
const certPath = commonUtils.getPathOfCACert();
|
|
@@ -138,6 +139,7 @@ SecWebSocket.prototype.init = function init() {
|
|
|
138
139
|
})
|
|
139
140
|
webSocket.on('close', () => {
|
|
140
141
|
logger.warn("WS connection closed");
|
|
142
|
+
this.obeyReconnect();
|
|
141
143
|
})
|
|
142
144
|
this.instance = webSocket;
|
|
143
145
|
};
|
|
@@ -172,7 +174,7 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
|
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
initTimeOut = setTimeout(() => {
|
|
175
|
-
|
|
177
|
+
|
|
176
178
|
logger.debug("Terminating ws instance and reconnecting");
|
|
177
179
|
try {
|
|
178
180
|
instance.terminate();
|
|
@@ -189,18 +191,21 @@ SecWebSocket.prototype.pingWS = async function pingWS() {
|
|
|
189
191
|
let currentTime = Date.now();
|
|
190
192
|
|
|
191
193
|
let timeDiffInPing = (currentTime - lastPongtime) / 1000;
|
|
192
|
-
|
|
194
|
+
logger.debug("Websocket ready state is:",self.instance.readyState)
|
|
195
|
+
|
|
196
|
+
if (timeDiffInPing > 40 && lastPongtime != 0 && self.instance.readyState > 1) {
|
|
193
197
|
self.obeyReconnect();
|
|
194
198
|
lastPongtime = 0;
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
201
|
+
|
|
197
202
|
try {
|
|
198
203
|
await promisify(self.instance, self.instance.ping)(ACK, true, function (err) {
|
|
199
204
|
if (err) {
|
|
200
205
|
logger.debug("Error while pinging:", err);
|
|
201
206
|
commonUtils.setWSHealthStatus('Error');
|
|
202
207
|
statusUtils.addErrortoBuffer(err);
|
|
203
|
-
|
|
208
|
+
|
|
204
209
|
}
|
|
205
210
|
});
|
|
206
211
|
} catch (err) {
|
|
@@ -258,7 +263,7 @@ SecWebSocket.prototype.dispatch = async function dispatch(event) {
|
|
|
258
263
|
hc.getInstance().exitEventStats.sent++;
|
|
259
264
|
}
|
|
260
265
|
|
|
261
|
-
})
|
|
266
|
+
})
|
|
262
267
|
} catch (error) {
|
|
263
268
|
logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error);
|
|
264
269
|
statusUtils.addErrortoBuffer(error);
|
|
@@ -101,6 +101,7 @@ function generateSecEvent(securityMetadata) {
|
|
|
101
101
|
const isAPIBlocked = false;
|
|
102
102
|
const event = new SecEvent(traceObject.sourceDetails.fileName, traceObject.sourceDetails.funcName, traceObject.sourceDetails.lineNumber, securityMetadata.interceptedArgs, securityMetadata.executionId, agentModule.applicationInfo, securityMetadata.request, securityMetadata.eventType, traceObject.sourceDetails.source, securityMetadata.eventCategory, metaData, stakTrace, apiId, isAPIBlocked);
|
|
103
103
|
event.metaData.isClientDetectedFromXFF = isClientDetectedFromXFF;
|
|
104
|
+
event.httpRequest.route = uri;
|
|
104
105
|
return event;
|
|
105
106
|
} catch (e) {
|
|
106
107
|
statusUtils.addErrortoBuffer(e);
|
|
@@ -98,7 +98,7 @@ function writeSnapshot() {
|
|
|
98
98
|
if (err) {
|
|
99
99
|
logger.debug(err.message);
|
|
100
100
|
const LogMessage = require('./LogMessage');
|
|
101
|
-
const logMessage = new LogMessage.logMessage("
|
|
101
|
+
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating snapshot file', __filename, err);
|
|
102
102
|
commonUtils.addLogEventtoBuffer(logMessage);
|
|
103
103
|
} else {
|
|
104
104
|
logger.info('Snapshot updated to file: %s', statusFile);
|
|
@@ -82,7 +82,7 @@ const initialize = function () {
|
|
|
82
82
|
try {
|
|
83
83
|
const framework = NRAgent.environment.get('Framework')[0];
|
|
84
84
|
const dispatcher = NRAgent.environment.get('Dispatcher')[0];
|
|
85
|
-
const logMessage = new LogMessage.logMessage("
|
|
85
|
+
const logMessage = new LogMessage.logMessage("INFO", `Detected framework: ${framework} and dispatcher: ${dispatcher}`, __filename, null);
|
|
86
86
|
commonUtils.addLogEventtoBuffer(logMessage);
|
|
87
87
|
} catch (error) {
|
|
88
88
|
logger.debug("Error while extracting data from APM environment",error);
|