@newrelic/security-agent 0.0.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 +0 -0
- package/CONTRIBUTING.md +23 -0
- package/LICENSE +43 -0
- package/README.md +98 -0
- package/THIRD_PARTY_NOTICES.md +2909 -0
- package/index.js +24 -0
- package/lib/instrumentation-security/core/constants.js +66 -0
- package/lib/instrumentation-security/core/event-constants.js +49 -0
- package/lib/instrumentation-security/core/request-manager.js +73 -0
- package/lib/instrumentation-security/core/route-manager.js +30 -0
- package/lib/instrumentation-security/core/sec-trace.js +134 -0
- package/lib/instrumentation-security/core/sec-utils.js +212 -0
- package/lib/instrumentation-security/core/security-metadata.js +34 -0
- package/lib/instrumentation-security/core/stack-trace.js +156 -0
- package/lib/instrumentation-security/hooks/director/nr-director.js +98 -0
- package/lib/instrumentation-security/hooks/express/nr-express.js +106 -0
- package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +155 -0
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +391 -0
- package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +131 -0
- package/lib/instrumentation-security/hooks/http/nr-http.js +461 -0
- package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +110 -0
- package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +81 -0
- package/lib/instrumentation-security/hooks/mongodb/common.js +239 -0
- package/lib/instrumentation-security/hooks/mongodb/constants.js +91 -0
- package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +49 -0
- package/lib/instrumentation-security/hooks/mongodb/v2-mongo.js +91 -0
- package/lib/instrumentation-security/hooks/mongodb/v3-mongo.js +36 -0
- package/lib/instrumentation-security/hooks/mongodb/v4-mongo.js +17 -0
- package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +232 -0
- package/lib/instrumentation-security/hooks/native/nr-childProcess.js +131 -0
- package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +131 -0
- package/lib/instrumentation-security/hooks/require/nr-require.js +93 -0
- package/lib/instrumentation-security/hooks/restify/nr-restify.js +104 -0
- package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +91 -0
- package/lib/instrumentation-security/index.js +171 -0
- package/lib/nr-security-agent/index.js +170 -0
- package/lib/nr-security-agent/lib/core/Auth-headers.js +58 -0
- package/lib/nr-security-agent/lib/core/ContainerProperties.js +42 -0
- package/lib/nr-security-agent/lib/core/ECSProperties.js +34 -0
- package/lib/nr-security-agent/lib/core/FuzzFailEvent.js +27 -0
- package/lib/nr-security-agent/lib/core/LinkingMetadata.js +21 -0
- package/lib/nr-security-agent/lib/core/LogMessage.js +47 -0
- package/lib/nr-security-agent/lib/core/PodProperties.js +36 -0
- package/lib/nr-security-agent/lib/core/Policy.js +105 -0
- package/lib/nr-security-agent/lib/core/agent-status.js +56 -0
- package/lib/nr-security-agent/lib/core/agent.js +146 -0
- package/lib/nr-security-agent/lib/core/applicationinfo.js +634 -0
- package/lib/nr-security-agent/lib/core/commonUtils.js +411 -0
- package/lib/nr-security-agent/lib/core/connections/aws-lambda-client.js +55 -0
- package/lib/nr-security-agent/lib/core/connections/restclient.js +20 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/index.js +25 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collecotor-policy-error-handler.js +18 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +20 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +171 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +47 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/no-op-handler.js +12 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +27 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/unsupported-agent-handler.js +18 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +14 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +443 -0
- package/lib/nr-security-agent/lib/core/event.js +186 -0
- package/lib/nr-security-agent/lib/core/health-check.js +159 -0
- package/lib/nr-security-agent/lib/core/json-file-handler.js +66 -0
- package/lib/nr-security-agent/lib/core/jsonValidator.js +149 -0
- package/lib/nr-security-agent/lib/core/lambda-delegated-task-service.js +90 -0
- package/lib/nr-security-agent/lib/core/logging.js +176 -0
- package/lib/nr-security-agent/lib/core/sec-agent-constants.js +173 -0
- package/lib/nr-security-agent/lib/core/sec-util.js +166 -0
- package/lib/nr-security-agent/lib/core/sha-size-util.js +338 -0
- package/lib/nr-security-agent/lib/core/sha_worker.js +24 -0
- package/lib/nr-security-agent/lib/core/startup-config.js +29 -0
- package/lib/nr-security-agent/lib/core/statusUtils.js +157 -0
- package/lib/nr-security-agent/lib/core/timeunit.js +44 -0
- package/lib/nr-security-agent/lib/core/websocket-client.js +335 -0
- package/lib/nr-security-agent/lib/core/worker-entities.js +82 -0
- package/lib/nr-security-agent/lib/core/worker.js +148 -0
- package/lib/nr-security-agent/lib/core/xss-utils.js +297 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/constants.js +12 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/index.js +15 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-sha-calculation.js +37 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-base.js +50 -0
- package/lib/nr-security-agent/lib/models/lambda-delegated-task/lambda-delegated-task-type.js +17 -0
- package/lib/nr-security-agent/resources/cert.pem +31 -0
- package/lib/nr-security-agent/resources/config.js +19 -0
- package/lib/nr-security-agent/resources/default-policy.yaml +36 -0
- package/lib/nr-security-api/index.js +111 -0
- package/package.json +95 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = initialize;
|
|
7
|
+
const requestManager = require("../../core/request-manager");
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const secUtils = require("../../core/sec-utils");
|
|
12
|
+
const API = require("../../../nr-security-api");
|
|
13
|
+
const securityMetaData = require('../../core/security-metadata');
|
|
14
|
+
const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
|
|
15
|
+
const logger = API.getLogger();
|
|
16
|
+
const { STRING, QUESTIONMARK, DOTDOTSLASH, UNDEFINED, SELF_FD_PATH, SLASH, SLASHDOTDOT, NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
|
|
17
|
+
const fsConsts = process.binding('constants').fs;
|
|
18
|
+
const agentModule = API.getSecAgent();
|
|
19
|
+
|
|
20
|
+
const requireHook = require('../require/nr-require');
|
|
21
|
+
|
|
22
|
+
const COPY_FILE = 'copyFile';
|
|
23
|
+
const RENAME = 'rename';
|
|
24
|
+
const FSReqCallback = 'FSReqCallback';
|
|
25
|
+
const FSReqWrap = 'FSReqWrap';
|
|
26
|
+
const OBJECT = 'object';
|
|
27
|
+
|
|
28
|
+
const functionsProbableToFA = [
|
|
29
|
+
"fstat",
|
|
30
|
+
"read",
|
|
31
|
+
"readBuffers",
|
|
32
|
+
"fdatasync",
|
|
33
|
+
"fsync",
|
|
34
|
+
"readdir",
|
|
35
|
+
"readlink",
|
|
36
|
+
"realpath",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const functionProbableToFI = [
|
|
40
|
+
"rename",
|
|
41
|
+
"ftruncate",
|
|
42
|
+
"rmdir",
|
|
43
|
+
"symlink",
|
|
44
|
+
"link",
|
|
45
|
+
"unlink",
|
|
46
|
+
"fchmod",
|
|
47
|
+
"chmod",
|
|
48
|
+
"lchown",
|
|
49
|
+
"fchown",
|
|
50
|
+
"chown",
|
|
51
|
+
"utimes",
|
|
52
|
+
"futimes",
|
|
53
|
+
"lutimes",
|
|
54
|
+
"mkdtemp",
|
|
55
|
+
"copyFile",
|
|
56
|
+
"mkdir",
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const promiseFunctionsProbableToFA = [
|
|
60
|
+
"open",
|
|
61
|
+
"readdir",
|
|
62
|
+
"readlink",
|
|
63
|
+
"readFile",
|
|
64
|
+
"lstat",
|
|
65
|
+
"stat",
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const promiseFunctionProbableToFI = [
|
|
69
|
+
"copyFile",
|
|
70
|
+
"rename",
|
|
71
|
+
"truncate",
|
|
72
|
+
"rmdir",
|
|
73
|
+
"mkdir",
|
|
74
|
+
"symlink",
|
|
75
|
+
"link",
|
|
76
|
+
"unlink",
|
|
77
|
+
"chmod",
|
|
78
|
+
"lchmod",
|
|
79
|
+
"lchown",
|
|
80
|
+
"chown",
|
|
81
|
+
"utimes",
|
|
82
|
+
"lutimes",
|
|
83
|
+
"mkdtemp",
|
|
84
|
+
"writeFile",
|
|
85
|
+
"appendFile",
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* init function to apply hook on fs module.
|
|
90
|
+
* @param {*} shim
|
|
91
|
+
* @param {*} mod
|
|
92
|
+
* @param {*} moduleName
|
|
93
|
+
*/
|
|
94
|
+
function initialize(shim, mod, moduleName) {
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
logger.info("Instrumenting FS module");
|
|
98
|
+
requireHook.initialize(shim)
|
|
99
|
+
const binding = process.binding("fs");
|
|
100
|
+
openHook(shim, binding, moduleName);
|
|
101
|
+
probableToFAHooks(shim, mod, moduleName);
|
|
102
|
+
probableToFIHooks(shim, mod, moduleName);
|
|
103
|
+
const FSPromise = mod.promises;
|
|
104
|
+
if (FSPromise) {
|
|
105
|
+
probablePromiseToFAHooks(shim, FSPromise, moduleName);
|
|
106
|
+
probablePromiseToFIHooks(shim, FSPromise, moduleName);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Wrapper for fs.open() function.
|
|
112
|
+
* @param {*} shim
|
|
113
|
+
* @param {*} mod
|
|
114
|
+
* @param {*} moduleName
|
|
115
|
+
*/
|
|
116
|
+
function openHook(shim, mod, moduleName) {
|
|
117
|
+
shim.wrap(mod, "open", function makeOpenWrapper(shim, fn) {
|
|
118
|
+
return function openWrapper() {
|
|
119
|
+
const parameters = Array.prototype.slice.apply(arguments);
|
|
120
|
+
const interceptedArgs = [arguments[0]];
|
|
121
|
+
shim.interceptedArgs = interceptedArgs;
|
|
122
|
+
const request = requestManager.getRequest(shim);
|
|
123
|
+
if ((parameters[0] != undefined) && (typeof parameters[0] !== STRING) && (typeof parameters[0] !== UNDEFINED) && (parameters[0] !== UNDEFINED)) {
|
|
124
|
+
try {
|
|
125
|
+
parameters[0] = fs.readlinkSync(SELF_FD_PATH.concat(parameters[0]));
|
|
126
|
+
} catch (err) {
|
|
127
|
+
parameters[0] = arguments[0];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (request && typeof parameters[0] === STRING) {
|
|
131
|
+
const policy = API.getPolicy();
|
|
132
|
+
const dynamicScanningFlag = policy.data ? policy.data.vulnerabilityScan.iastScan.enabled : false;
|
|
133
|
+
const url = request.url;
|
|
134
|
+
const decodedURL = decodeURI(url);
|
|
135
|
+
const trimedURL = url.split(QUESTIONMARK)[0];
|
|
136
|
+
if (!trimedURL.includes(path.basename(parameters[0])) || decodedURL.includes(DOTDOTSLASH) || dynamicScanningFlag) {
|
|
137
|
+
try {
|
|
138
|
+
if (parameters[0].startsWith(DOTDOTSLADH)) {
|
|
139
|
+
parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + SLASH + parameters[0];
|
|
140
|
+
} else if (parameters[0].startsWith(SLASHDOTDOT)) {
|
|
141
|
+
parameters[0] = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath + parameters[0];
|
|
142
|
+
} else {
|
|
143
|
+
parameters[0] = path.resolve(parameters[0]);
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
149
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCaseType(parameters[1], parameters[0]), EVENT_CATEGORY.FILE)
|
|
150
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
151
|
+
API.sendEvent(this.secEvent);
|
|
152
|
+
const callbackFlag = isCallback(arguments);
|
|
153
|
+
if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
154
|
+
callbackHook(shim, arguments[3], 'oncomplete', this.secEvent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
const result = fn.apply(this, arguments);
|
|
160
|
+
if(result>0 && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]){
|
|
161
|
+
//here generate exit event.
|
|
162
|
+
API.generateExitEvent(this.secEvent);
|
|
163
|
+
delete this.secEvent;
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Wrapper to hook all the function provided in list functionsProbableToFA
|
|
172
|
+
* @param {*} shim
|
|
173
|
+
* @param {*} mod
|
|
174
|
+
* @param {*} moduleName
|
|
175
|
+
*/
|
|
176
|
+
function probableToFAHooks(shim, mod, moduleName) {
|
|
177
|
+
functionsProbableToFA.forEach(function (fun) {
|
|
178
|
+
shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
|
|
179
|
+
return function FAWrapper() {
|
|
180
|
+
const interceptedArgs = [arguments[0]];
|
|
181
|
+
shim.interceptedArgs = interceptedArgs;
|
|
182
|
+
const request = requestManager.getRequest(shim);
|
|
183
|
+
if (request && typeof arguments[0] === STRING) {
|
|
184
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
185
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
|
|
186
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
187
|
+
API.sendEvent(this.secEvent);
|
|
188
|
+
const callbackFlag = isCallback(arguments);
|
|
189
|
+
if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
190
|
+
callbackHook(shim, arguments, arguments.length-1, this.secEvent);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return fn.apply(this, arguments);
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Wrapper to hook all the function provided in list functionProbableToFI
|
|
201
|
+
* @param {*} shim
|
|
202
|
+
* @param {*} mod
|
|
203
|
+
* @param {*} moduleName
|
|
204
|
+
*/
|
|
205
|
+
function probableToFIHooks(shim, mod, moduleName) {
|
|
206
|
+
functionProbableToFI.forEach(function (fun) {
|
|
207
|
+
shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
|
|
208
|
+
return function FIWrapper() {
|
|
209
|
+
const interceptedArgs = [arguments[0]];
|
|
210
|
+
shim.interceptedArgs = interceptedArgs;
|
|
211
|
+
const request = requestManager.getRequest(shim);
|
|
212
|
+
if (request && typeof arguments[0] === STRING) {
|
|
213
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
214
|
+
if (fun === COPY_FILE || fun === RENAME) {
|
|
215
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
|
|
216
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
217
|
+
API.sendEvent(this.secEvent);
|
|
218
|
+
}
|
|
219
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCase(arguments[0]), EVENT_CATEGORY.FILE)
|
|
220
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
221
|
+
API.sendEvent(this.secEvent);
|
|
222
|
+
const callbackFlag = isCallback(arguments);
|
|
223
|
+
if (callbackFlag && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
224
|
+
callbackHook(shim, arguments, arguments.length-1, this.secEvent);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return fn.apply(this, arguments);
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Wrapper to hook all the function provided in list promiseFunctionsProbableToFA
|
|
235
|
+
* @param {*} shim
|
|
236
|
+
* @param {*} mod
|
|
237
|
+
* @param {*} moduleName
|
|
238
|
+
*/
|
|
239
|
+
function probablePromiseToFAHooks(shim, mod, moduleName) {
|
|
240
|
+
promiseFunctionsProbableToFA.forEach(function (fun) {
|
|
241
|
+
shim.wrap(mod, fun, function makeFAWrapper(shim, fn) {
|
|
242
|
+
return function FAWrapper() {
|
|
243
|
+
const interceptedArgs = [arguments[0]];
|
|
244
|
+
shim.interceptedArgs = interceptedArgs;
|
|
245
|
+
const request = requestManager.getRequest(shim);
|
|
246
|
+
if (request && typeof arguments[0] === STRING) {
|
|
247
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
248
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.FILE_OPERATION, EVENT_CATEGORY.FILE)
|
|
249
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
250
|
+
API.sendEvent(this.secEvent);
|
|
251
|
+
}
|
|
252
|
+
return fn.apply(this, arguments);
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Wrapper to hook all the function provided in list promiseFunctionProbableToFI
|
|
260
|
+
* @param {*} shim
|
|
261
|
+
* @param {*} mod
|
|
262
|
+
* @param {*} moduleName
|
|
263
|
+
*/
|
|
264
|
+
function probablePromiseToFIHooks(shim, mod, moduleName) {
|
|
265
|
+
promiseFunctionProbableToFI.forEach(function (fun) {
|
|
266
|
+
shim.wrap(mod, fun, function makeFIWrapper(shim, fn) {
|
|
267
|
+
return function FIWrapper() {
|
|
268
|
+
const interceptedArgs = [arguments[0]];
|
|
269
|
+
shim.interceptedArgs = interceptedArgs;
|
|
270
|
+
const request = requestManager.getRequest(shim);
|
|
271
|
+
if (request && typeof arguments[0] === STRING) {
|
|
272
|
+
const traceObject = secUtils.getTraceObject(shim);
|
|
273
|
+
if (fun === COPY_FILE || fun === RENAME) {
|
|
274
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, arguments[1], traceObject, secUtils.getExecutionId(), getCase(arguments[1]), EVENT_CATEGORY.FILE)
|
|
275
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
276
|
+
API.sendEvent(this.secEvent);
|
|
277
|
+
}
|
|
278
|
+
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), getCase(arguments[0]), EVENT_CATEGORY.FILE)
|
|
279
|
+
this.secEvent = API.generateSecEvent(secMetadata);
|
|
280
|
+
API.sendEvent(this.secEvent);
|
|
281
|
+
}
|
|
282
|
+
return fn.apply(this, arguments);
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Utility to get case type based on flag and file path in case of file open operation
|
|
290
|
+
* @param {*} flag
|
|
291
|
+
* @param {*} filePath
|
|
292
|
+
* @returns
|
|
293
|
+
*/
|
|
294
|
+
function getCaseType(flag, filePath) {
|
|
295
|
+
if (flag) {
|
|
296
|
+
switch (flag) {
|
|
297
|
+
case fsConsts.O_RDONLY:
|
|
298
|
+
case fsConsts.O_RDONLY | fsConsts.O_SYNC:
|
|
299
|
+
return EVENT_TYPE.FILE_OPERATION;
|
|
300
|
+
case fsConsts.O_RDWR:
|
|
301
|
+
case fsConsts.O_RDWR | fsConsts.O_SYNC:
|
|
302
|
+
case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_WRONLY:
|
|
303
|
+
case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_EXCL:
|
|
304
|
+
case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_RDWR:
|
|
305
|
+
case fsConsts.O_TRUNC | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_EXCL:
|
|
306
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY:
|
|
307
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_EXCL:
|
|
308
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_WRONLY | fsConsts.O_SYNC:
|
|
309
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR:
|
|
310
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_EXCL:
|
|
311
|
+
case fsConsts.O_APPEND | fsConsts.O_CREAT | fsConsts.O_RDWR | fsConsts.O_SYNC:
|
|
312
|
+
filePath = path.resolve(filePath);
|
|
313
|
+
const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
|
|
314
|
+
const isInPath = isPathInside(filePath, appPath);
|
|
315
|
+
if (typeof filePath === STRING && isInPath) {
|
|
316
|
+
return EVENT_TYPE.FILE_INTEGRITY;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return EVENT_TYPE.FILE_OPERATION;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Utility to get case type based on provided file path
|
|
325
|
+
* @param {*} filePath
|
|
326
|
+
* @returns
|
|
327
|
+
*/
|
|
328
|
+
function getCase(filePath) {
|
|
329
|
+
filePath = path.resolve(filePath);
|
|
330
|
+
const appPath = agentModule.applicationInfo.serverInfo.deployedApplications[0].deployedPath;
|
|
331
|
+
const isInPath = isPathInside(filePath, appPath);
|
|
332
|
+
if (typeof filePath === STRING && isInPath) {
|
|
333
|
+
return EVENT_TYPE.FILE_INTEGRITY;
|
|
334
|
+
} else {
|
|
335
|
+
return EVENT_TYPE.FILE_OPERATION;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Utility to check if child path is inside parent path
|
|
341
|
+
* @param {*} childPath
|
|
342
|
+
* @param {*} parentPath
|
|
343
|
+
* @returns
|
|
344
|
+
*/
|
|
345
|
+
function isPathInside(childPath, parentPath) {
|
|
346
|
+
childPath = path.resolve(childPath);
|
|
347
|
+
parentPath = path.resolve(parentPath);
|
|
348
|
+
|
|
349
|
+
if (childPath === parentPath) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
childPath += path.sep;
|
|
354
|
+
parentPath += path.sep;
|
|
355
|
+
|
|
356
|
+
return childPath.startsWith(parentPath);
|
|
357
|
+
}
|
|
358
|
+
function isCallback(function_args) {
|
|
359
|
+
try {
|
|
360
|
+
const obj = function_args[function_args.length - 1];
|
|
361
|
+
if (obj && typeof function_args[function_args.length - 1] === OBJECT && (obj.constructor.name === FSReqCallback || obj.constructor.name === FSReqWrap)) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Callback hook to generate exit event
|
|
372
|
+
* @param {*} shim
|
|
373
|
+
* @param {*} mod
|
|
374
|
+
* @param {*} fun
|
|
375
|
+
* @param {*} secEvent
|
|
376
|
+
*/
|
|
377
|
+
function callbackHook(shim, mod, fun, secEvent) {
|
|
378
|
+
shim.secEvent = secEvent;
|
|
379
|
+
shim.wrap(mod, fun, function callbackWrapper(shim, fn) {
|
|
380
|
+
if (!shim.isFunction(fn)) {
|
|
381
|
+
return fn;
|
|
382
|
+
}
|
|
383
|
+
return function wrapper() {
|
|
384
|
+
if ((arguments[0] === null || arguments[0] === undefined) && shim.secEvent) {
|
|
385
|
+
API.generateExitEvent(shim.secEvent);
|
|
386
|
+
delete shim.secEvent;
|
|
387
|
+
}
|
|
388
|
+
return fn.apply(this, arguments);
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
'use strict'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Express middleware generates traces where middleware are considered siblings
|
|
11
|
+
* (ended on 'next' invocation) and not nested. Middlware are nested below the
|
|
12
|
+
* routers they are mounted to.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const requestManager = require("../../core/request-manager");
|
|
16
|
+
const secUtils = require('../../core/sec-utils');
|
|
17
|
+
const { ATTHERATE, DOUBLE_DOLLAR } = require('../../core/constants');
|
|
18
|
+
const lodash = require('lodash');
|
|
19
|
+
const routeManager = require('../../core/route-manager');
|
|
20
|
+
const API = require("../../../nr-security-api");
|
|
21
|
+
const logger = API.getLogger();
|
|
22
|
+
|
|
23
|
+
module.exports = function initialize(shim, hapi, moduleName) {
|
|
24
|
+
if (!hapi || !shim) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
logger.info("Instrumenting Hapi")
|
|
29
|
+
// 'Server' and 'server' both point to the same export,
|
|
30
|
+
// but we can't make any assumption about which will be used.
|
|
31
|
+
// Since we wrap the prototype, the second wrap should exit early.
|
|
32
|
+
shim.wrapReturn(hapi, 'server', serverFactoryWrapper)
|
|
33
|
+
shim.wrapReturn(hapi, 'Server', serverFactoryWrapper)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function serverFactoryWrapper(shim, fn, fnName, server) {
|
|
37
|
+
serverPostConstructor.call(server, shim)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function serverPostConstructor(shim) {
|
|
41
|
+
const proto = Object.getPrototypeOf(this)
|
|
42
|
+
wrapProtoRoute(shim, proto)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Wrapper for route
|
|
47
|
+
* @param {*} shim
|
|
48
|
+
* @param {*} proto
|
|
49
|
+
*/
|
|
50
|
+
function wrapProtoRoute(shim, proto) {
|
|
51
|
+
shim.wrap(proto, 'route', function wrapRoute(shim, original) {
|
|
52
|
+
return function wrappedRoute() {
|
|
53
|
+
const args = shim.argsToArray.apply(shim, arguments)
|
|
54
|
+
if (!shim.isObject(args[0])) {
|
|
55
|
+
return original.apply(this, args)
|
|
56
|
+
}
|
|
57
|
+
const obj = args[0];
|
|
58
|
+
for (let i = 0; i < obj.length; i++) {
|
|
59
|
+
try {
|
|
60
|
+
const method = lodash.upperCase(obj[i].method);
|
|
61
|
+
const path = obj[i].path;
|
|
62
|
+
const stakTrace = secUtils.traceElementForRoute();
|
|
63
|
+
const splittedStack = stakTrace[0].split(DOUBLE_DOLLAR);
|
|
64
|
+
const key = method + ATTHERATE + path;
|
|
65
|
+
routeManager.setRoute(key, splittedStack[0]);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
if (obj[i].handler) {
|
|
70
|
+
handlerWrapper(shim, obj[i]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return original.apply(this, args)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Wrapper to hook each handler corresponding to route
|
|
81
|
+
* @param {*} shim
|
|
82
|
+
* @param {*} mod
|
|
83
|
+
*/
|
|
84
|
+
function handlerWrapper(shim, mod) {
|
|
85
|
+
shim.wrap(mod, 'handler', function wrapHandler(shim, original) {
|
|
86
|
+
if (!shim.isFunction(original)) {
|
|
87
|
+
return original
|
|
88
|
+
}
|
|
89
|
+
return function wrappedHandler() {
|
|
90
|
+
extractParams(shim, arguments[0]);
|
|
91
|
+
return original.apply(this, arguments);
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Utility function to parse request obejct intercepted from express.router.process_params() method hook
|
|
99
|
+
* @param {*} shim
|
|
100
|
+
* @param {*} req
|
|
101
|
+
*/
|
|
102
|
+
function extractParams(shim, req) {
|
|
103
|
+
const segment = shim.getActiveSegment();
|
|
104
|
+
if (segment && segment.transaction) {
|
|
105
|
+
let request = requestManager.getRequestFromId(segment.transaction.id);
|
|
106
|
+
if (req.params && request) {
|
|
107
|
+
Object.keys(req.params).forEach(function (key) {
|
|
108
|
+
if (req.params[key]) {
|
|
109
|
+
if (!request.parameterMap[key]) {
|
|
110
|
+
request.parameterMap[key] = new Array(req.params[key].toString());
|
|
111
|
+
requestManager.setRequest(segment.transaction.id, request);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (req.query && request) {
|
|
117
|
+
Object.keys(req.query).forEach(function (key) {
|
|
118
|
+
if (req.query[key]) {
|
|
119
|
+
if (!request.parameterMap[key]) {
|
|
120
|
+
request.parameterMap[key] = new Array(req.query[key].toString());
|
|
121
|
+
requestManager.setRequest(segment.transaction.id, request);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (req.route && req.route.path && request) {
|
|
127
|
+
request.uri = req.route.path;
|
|
128
|
+
requestManager.setRequest(segment.transaction.id, request);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|