@newrelic/security-agent 1.1.0 → 1.1.1
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 +4 -0
- package/README.md +0 -1
- package/THIRD_PARTY_NOTICES.md +48 -1
- package/lib/instrumentation-security/index.js +0 -47
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +11 -31
- package/package.json +1 -3
- package/lib/instrumentation-security/core/grpc-utils.js +0 -53
- package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +0 -284
- package/lib/nr-security-agent/lib/core/connections/grpcClient.js +0 -111
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -68,7 +68,6 @@ The [Developer docs](http://newrelic.github.io/node-newrelic/) for writing instr
|
|
|
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
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)
|
|
72
71
|
|
|
73
72
|
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/).
|
|
74
73
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -64,7 +64,9 @@ code, the source code can be found at [https://github.com/newrelic/csec-node-age
|
|
|
64
64
|
* [request](#request)
|
|
65
65
|
* [restify](#restify)
|
|
66
66
|
* [sinon](#sinon)
|
|
67
|
+
* [stream](#stream)
|
|
67
68
|
* [tap](#tap)
|
|
69
|
+
* [undici](#undici)
|
|
68
70
|
* [xmldom](#xmldom)
|
|
69
71
|
* [xpath.js](#xpathjs)
|
|
70
72
|
* [xpath](#xpath)
|
|
@@ -74,7 +76,7 @@ code, the source code can be found at [https://github.com/newrelic/csec-node-age
|
|
|
74
76
|
|
|
75
77
|
### axios
|
|
76
78
|
|
|
77
|
-
This product includes source derived from [axios](https://github.com/axios/axios) ([v1.6.
|
|
79
|
+
This product includes source derived from [axios](https://github.com/axios/axios) ([v1.6.8](https://github.com/axios/axios/tree/v1.6.8)), distributed under the [MIT License](https://github.com/axios/axios/blob/v1.6.8/LICENSE):
|
|
78
80
|
|
|
79
81
|
```
|
|
80
82
|
# Copyright (c) 2014-present Matt Zabriskie & Collaborators
|
|
@@ -2588,6 +2590,22 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
2588
2590
|
|
|
2589
2591
|
```
|
|
2590
2592
|
|
|
2593
|
+
### stream
|
|
2594
|
+
|
|
2595
|
+
This product includes source derived from [stream](https://github.com/juliangruber/stream) ([v0.0.2](https://github.com/juliangruber/stream/tree/v0.0.2)), distributed under the [MIT License](https://github.com/juliangruber/stream/blob/v0.0.2/README.md):
|
|
2596
|
+
|
|
2597
|
+
```
|
|
2598
|
+
MIT License
|
|
2599
|
+
|
|
2600
|
+
Copyright (c) <year> <copyright holders>
|
|
2601
|
+
|
|
2602
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
2603
|
+
|
|
2604
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
2605
|
+
|
|
2606
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
2607
|
+
```
|
|
2608
|
+
|
|
2591
2609
|
### tap
|
|
2592
2610
|
|
|
2593
2611
|
This product includes source derived from [tap](https://github.com/tapjs/node-tap) ([v16.3.7](https://github.com/tapjs/node-tap/tree/v16.3.7)), distributed under the [ISC License](https://github.com/tapjs/node-tap/blob/v16.3.7/LICENSE):
|
|
@@ -2611,6 +2629,35 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
2611
2629
|
|
|
2612
2630
|
```
|
|
2613
2631
|
|
|
2632
|
+
### undici
|
|
2633
|
+
|
|
2634
|
+
This product includes source derived from [undici](https://github.com/nodejs/undici) ([v5.28.3](https://github.com/nodejs/undici/tree/v5.28.3)), distributed under the [MIT License](https://github.com/nodejs/undici/blob/v5.28.3/LICENSE):
|
|
2635
|
+
|
|
2636
|
+
```
|
|
2637
|
+
MIT License
|
|
2638
|
+
|
|
2639
|
+
Copyright (c) Matteo Collina and Undici contributors
|
|
2640
|
+
|
|
2641
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2642
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
2643
|
+
in the Software without restriction, including without limitation the rights
|
|
2644
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
2645
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
2646
|
+
furnished to do so, subject to the following conditions:
|
|
2647
|
+
|
|
2648
|
+
The above copyright notice and this permission notice shall be included in all
|
|
2649
|
+
copies or substantial portions of the Software.
|
|
2650
|
+
|
|
2651
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
2652
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
2653
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
2654
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
2655
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
2656
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
2657
|
+
SOFTWARE.
|
|
2658
|
+
|
|
2659
|
+
```
|
|
2660
|
+
|
|
2614
2661
|
### xmldom
|
|
2615
2662
|
|
|
2616
2663
|
This product includes source derived from [xmldom](https://github.com/xmldom/xmldom) ([v0.6.0](https://github.com/xmldom/xmldom/tree/v0.6.0)), distributed under the [MIT License](https://github.com/xmldom/xmldom/blob/v0.6.0/LICENSE):
|
|
@@ -200,55 +200,8 @@ newrelic.instrumentWebframework({
|
|
|
200
200
|
})
|
|
201
201
|
|
|
202
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
|
-
})
|
|
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
|
-
})
|
|
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
203
|
|
|
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
204
|
|
|
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
|
-
})
|
|
252
205
|
|
|
253
206
|
|
|
254
207
|
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
const stringify = require('fast-safe-stringify');
|
|
8
8
|
|
|
9
9
|
const restClient = require('../../restclient');
|
|
10
|
-
const grpcClient = require('../../grpcClient');
|
|
11
10
|
const { Agent } = require('../../../agent');
|
|
12
11
|
const { FuzzFailEvent } = require('../../../FuzzFailEvent');
|
|
13
12
|
const { CSEC_HOME_TMP, COLON, NR_CSEC_FUZZ_REQUEST_ID, SLASH, CSEC_SEP, SEVERE } = require('../../../sec-agent-constants');
|
|
@@ -21,6 +20,9 @@ const LogMessage = require('../../../LogMessage');
|
|
|
21
20
|
require('dns').setDefaultResultOrder('ipv4first')
|
|
22
21
|
const PolicyManager = require('../../../Policy');
|
|
23
22
|
|
|
23
|
+
const {
|
|
24
|
+
IS_LAMBDA_ENV,
|
|
25
|
+
AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER } = require('../../../sec-agent-constants');
|
|
24
26
|
const statusUtils = require('../../../statusUtils');
|
|
25
27
|
|
|
26
28
|
const find = `${SLASH}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
|
|
@@ -123,16 +125,13 @@ function handler(json) {
|
|
|
123
125
|
function handleFuzzRequest(fuzzDetails) {
|
|
124
126
|
const { fuzzRequest } = fuzzDetails;
|
|
125
127
|
try {
|
|
126
|
-
if (
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
IASTUtil.removePendingRequestId(fuzzRequest.id);
|
|
128
|
+
if (IS_LAMBDA_ENV) {
|
|
129
|
+
const qualifiedARN = Agent.getAgent().applicationInfo.applicationUUID;
|
|
130
|
+
const splitArn = String.prototype.split.call(qualifiedARN, ':');
|
|
131
|
+
const arn = splitArn.slice(0, splitArn.length - 1).join(':');
|
|
132
|
+
const ver = process.env[AWS_LAMBDA_FUNCTION_VERSION_ENV_IDENTIFIER];
|
|
133
|
+
logger.info('Invoking Lambda:: ARN: ' + arn);
|
|
134
|
+
logger.info('Invoking Lambda:: Version: ' + ver);
|
|
136
135
|
|
|
137
136
|
} else {
|
|
138
137
|
const config = parseAxiosHttpRequestToFuzz(fuzzRequest);
|
|
@@ -154,6 +153,7 @@ function handleFuzzRequest(fuzzDetails) {
|
|
|
154
153
|
|
|
155
154
|
|
|
156
155
|
logger.info('Firing http request:: URL: ' + config.url);
|
|
156
|
+
|
|
157
157
|
const response = restClient.fireRequest(config);
|
|
158
158
|
handleFuzzResponse(response, fuzzDetails);
|
|
159
159
|
}
|
|
@@ -210,26 +210,6 @@ 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
|
-
}
|
|
233
213
|
|
|
234
214
|
|
|
235
215
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/security-agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "New Relic Security Agent for Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"jsonVersion": "1.1.1",
|
|
@@ -58,8 +58,6 @@
|
|
|
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",
|
|
63
61
|
"@hapi/hapi": "^21.3.0",
|
|
64
62
|
"@koa/router": "^12.0.0",
|
|
65
63
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
@@ -1,53 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,284 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
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
|
-
};
|