@newrelic/security-agent 1.1.0 → 1.2.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.
@@ -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
- };