@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,443 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const WebSocket = require('ws');
|
|
9
|
+
const RingBuffer = require('ringbufferjs');
|
|
10
|
+
const stringify = require('fast-safe-stringify');
|
|
11
|
+
const { EventEmitter } = require('events');
|
|
12
|
+
|
|
13
|
+
const { Agent } = require('../../agent');
|
|
14
|
+
const logs = require('../../logging');
|
|
15
|
+
const hc = require('../../health-check');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { promisify } = require('../../sec-util');
|
|
19
|
+
const ResponseHandler = require('./response');
|
|
20
|
+
const { EMPTY_APPLICATION_UUID, LOG_MESSAGES } = require('../../sec-agent-constants');
|
|
21
|
+
const statusUtils = require('../../statusUtils');
|
|
22
|
+
const commonUtils = require('../../commonUtils');
|
|
23
|
+
|
|
24
|
+
const BUSY = 'busy';
|
|
25
|
+
const IDLE = 'idle';
|
|
26
|
+
const CONNECTING = 'connecting';
|
|
27
|
+
const WS_ON_OPEN = 'open';
|
|
28
|
+
const WS_ON_MSG = 'message';
|
|
29
|
+
const WS_ON_ERR = 'error';
|
|
30
|
+
const ACK = 'ACK';
|
|
31
|
+
const INIT_PING_RETRY_DELAY_ON_FAIL = 200; // in ms
|
|
32
|
+
const RECONNECTION_DELAY = 500; // ms
|
|
33
|
+
const APPLICATION_INFO_JSON_NAME = 'applicationinfo';
|
|
34
|
+
let APP_INFO;
|
|
35
|
+
|
|
36
|
+
const queue = new RingBuffer(1000);
|
|
37
|
+
let logger = logs.getLogger();
|
|
38
|
+
const initLogger = logs.getInitLogger();
|
|
39
|
+
let webSocket;
|
|
40
|
+
let pingIntervalConst;
|
|
41
|
+
let initTimeOut;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* defines the WebSocket callbacks object
|
|
45
|
+
*
|
|
46
|
+
* @param {Function} onOpen
|
|
47
|
+
* @param {Function} onMessage
|
|
48
|
+
* @param {Function} onError
|
|
49
|
+
*/
|
|
50
|
+
function WSCallbacks(onOpen, onMessage, onError) {
|
|
51
|
+
this.onOpen = onOpen;
|
|
52
|
+
this.onMessage = onMessage;
|
|
53
|
+
this.onError = onError;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* This defines the websocket and related
|
|
61
|
+
* methods
|
|
62
|
+
*
|
|
63
|
+
* @param {String} uri - to connect to
|
|
64
|
+
* @param {WSCallbacks} callbacks - on open, on message and on error callbacks
|
|
65
|
+
*/
|
|
66
|
+
function SecWebSocket(uri, callbacks = {}, logr = null) {
|
|
67
|
+
this.instance = undefined;
|
|
68
|
+
this.isICReadyForEvents = false;
|
|
69
|
+
this.uri = uri;
|
|
70
|
+
this.event = new EventEmitter();
|
|
71
|
+
this.openCB = (callbacks.onOpen || defaultOnOpenCB()).bind(this);
|
|
72
|
+
this.msgCB = (callbacks.onMessage || defaultOnMsgCB()).bind(this);
|
|
73
|
+
this.errCB = (callbacks.onError || defaultOnErrCB()).bind(this);
|
|
74
|
+
logger = logr || logger;
|
|
75
|
+
this.wsRetryCount = 0;
|
|
76
|
+
this.init();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
SecWebSocket.prototype = {};
|
|
80
|
+
SecWebSocket.prototype.constructor = SecWebSocket;
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Initiates a new web socket connection.
|
|
85
|
+
*/
|
|
86
|
+
SecWebSocket.prototype.init = function init() {
|
|
87
|
+
if (webSocket) {
|
|
88
|
+
try {
|
|
89
|
+
webSocket.removeAllListeners('open');
|
|
90
|
+
webSocket.removeAllListeners('close');
|
|
91
|
+
webSocket.removeAllListeners('message');
|
|
92
|
+
webSocket.terminate();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger.debug("Error while terminating webSocket instance", error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setWebSocketConn();
|
|
98
|
+
this.setValidatorNotReadyForEvents();
|
|
99
|
+
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
100
|
+
const authHeaders = require('../../Auth-headers').getInstance();
|
|
101
|
+
initLogger.info(`[STEP-4][BEGIN][WS] Connecting to Prevent-Web service at ${validatorService}`);
|
|
102
|
+
let cert = ''
|
|
103
|
+
try {
|
|
104
|
+
const certPath = commonUtils.getPathOfCACert();
|
|
105
|
+
cert = fs.readFileSync(certPath, 'utf8');
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error("Error in reading certificate:", error);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
webSocket = new WebSocket(validatorService, null, { headers: authHeaders, cert: cert });
|
|
111
|
+
|
|
112
|
+
webSocket.on(WS_ON_OPEN, this.openCB);
|
|
113
|
+
webSocket.on(WS_ON_MSG, this.msgCB);
|
|
114
|
+
webSocket.on(WS_ON_ERR, this.errCB);
|
|
115
|
+
this.instance = webSocket;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Re-initiates the websocket by disconnecting
|
|
120
|
+
* and initiating a new websoc connection.
|
|
121
|
+
*/
|
|
122
|
+
SecWebSocket.prototype.reconnect = function reconnect() {
|
|
123
|
+
const { instance } = this;
|
|
124
|
+
|
|
125
|
+
if (instance.readyState === WebSocket.CONNECTING &&
|
|
126
|
+
process.env.CSEC_WS_STATE === CONNECTING) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
setWebSocketConn();
|
|
130
|
+
this.setValidatorNotReadyForEvents();
|
|
131
|
+
|
|
132
|
+
if (instance.readyState === WebSocket.OPEN) {
|
|
133
|
+
this.openCB();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
process.env.WS_HEALTH = 'Error';
|
|
137
|
+
logger.debug(LOG_MESSAGES.DETECTED_BROKEN_CONN);
|
|
138
|
+
this.obeyReconnect();
|
|
139
|
+
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
|
|
143
|
+
const { instance } = this;
|
|
144
|
+
if(initTimeOut){
|
|
145
|
+
clearTimeout(initTimeOut);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
initTimeOut = setTimeout(() => {
|
|
149
|
+
logger.debug("Terminating ws instance and reconnecting");
|
|
150
|
+
try {
|
|
151
|
+
instance.terminate();
|
|
152
|
+
} catch (err) {
|
|
153
|
+
logger.debug("Error while terminating ws instance");
|
|
154
|
+
}
|
|
155
|
+
this.init();
|
|
156
|
+
}, 15000)
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
SecWebSocket.prototype.pingWS = async function pingWS() {
|
|
161
|
+
const self = this;
|
|
162
|
+
try {
|
|
163
|
+
await promisify(self.instance, self.instance.ping)(ACK, true, function (err) {
|
|
164
|
+
if (err) {
|
|
165
|
+
logger.debug("Error while pinging:", err);
|
|
166
|
+
self.obeyReconnect();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
} catch (err) {
|
|
170
|
+
logger.debug("error in ping:", err);
|
|
171
|
+
statusUtils.addErrortoBuffer(err);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* sends the event over the websocket instance.
|
|
178
|
+
* @param {JSON} event
|
|
179
|
+
*/
|
|
180
|
+
SecWebSocket.prototype.dispatch = async function dispatch(event) {
|
|
181
|
+
if (event.jsonName === APPLICATION_INFO_JSON_NAME) {
|
|
182
|
+
APP_INFO = event;
|
|
183
|
+
}
|
|
184
|
+
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
|
185
|
+
const eventStr = stringify(event);
|
|
186
|
+
if (!this.instance ||
|
|
187
|
+
this.instance.readyState !== WebSocket.OPEN ||
|
|
188
|
+
!this.isICReadyForEvents
|
|
189
|
+
) {
|
|
190
|
+
handleDispatchFailure(this, event, eventStr);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
aquireWebSocket();
|
|
195
|
+
logger.debug(LOG_MESSAGES.EVENT_SENT + eventStr);
|
|
196
|
+
await promisify(this.instance, this.instance.send)(eventStr, { mask: true });
|
|
197
|
+
releaseWebSocket();
|
|
198
|
+
} catch (error) {
|
|
199
|
+
logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error);
|
|
200
|
+
statusUtils.addErrortoBuffer(error);
|
|
201
|
+
handleDispatchFailure(this, event, eventStr);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* sends the applicationInfor over the websocket instance.
|
|
207
|
+
* @param {JSON} applicationInfoJSON
|
|
208
|
+
*/
|
|
209
|
+
SecWebSocket.prototype.dispatchApplicationInfo = async function dispatchApplicationInfo(applicationInfoJSON) {
|
|
210
|
+
const eventStr = stringify(applicationInfoJSON);
|
|
211
|
+
if (applicationInfoJSON.applicationUUID === EMPTY_APPLICATION_UUID) {
|
|
212
|
+
throw new Error('ApplicationInfo not completely Initialized, No application identification detail found!');
|
|
213
|
+
}
|
|
214
|
+
if (this.instance.readyState !== WebSocket.OPEN) {
|
|
215
|
+
logger.warn('Tried to send ApplicationInfo when socket was not open');
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
await promisify(this.instance, this.instance.send)(eventStr, { mask: true });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
statusUtils.addErrortoBuffer(error);
|
|
221
|
+
logger.warn('Error while sending applicationInfo:', error);
|
|
222
|
+
this.obeyReconnect();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* this sets the isICReadyForEvents property
|
|
230
|
+
*/
|
|
231
|
+
SecWebSocket.prototype.setICReadyForEvents = function setICReadyForEvents() {
|
|
232
|
+
this.isICReadyForEvents = true;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* this unsets the isICReadyForEvents property
|
|
237
|
+
*/
|
|
238
|
+
SecWebSocket.prototype.setValidatorNotReadyForEvents = function setValidatorNotReadyForEvents() {
|
|
239
|
+
this.isICReadyForEvents = false;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* This would flush all the events out of
|
|
244
|
+
* event queue and publish to IC.
|
|
245
|
+
*
|
|
246
|
+
* If the send fails there is no way to
|
|
247
|
+
* recover these events.
|
|
248
|
+
*/
|
|
249
|
+
SecWebSocket.prototype.flushEventQueue = async function flushEventQueue() {
|
|
250
|
+
aquireWebSocket();
|
|
251
|
+
try {
|
|
252
|
+
const sentEvents = [];
|
|
253
|
+
for (let counter = 0; counter < queue.size(); counter += 1) {
|
|
254
|
+
hc.getInstance().registerEventProcessed();
|
|
255
|
+
sentEvents.push(promisify(this.instance, this.instance.send)(queue.deq(), { mask: true }));
|
|
256
|
+
}
|
|
257
|
+
const results = await Promise.all(sentEvents);
|
|
258
|
+
hc.getInstance().registerEventsSent(results.length);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
statusUtils.addErrortoBuffer(err);
|
|
261
|
+
logger.error('Flush failed, reception of all events on IC couldn\'t be ensured.');
|
|
262
|
+
} finally {
|
|
263
|
+
releaseWebSocket();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* this handles the event dispatch failure
|
|
269
|
+
*
|
|
270
|
+
* @param {SecWebSocket} self
|
|
271
|
+
* @param {String} eventStr
|
|
272
|
+
*/
|
|
273
|
+
const handleDispatchFailure = (self, event, eventStr) => {
|
|
274
|
+
// we would want to drop applicationinfo events as the latest
|
|
275
|
+
// applicationinfo would anyway be relayed on reconnection
|
|
276
|
+
// so feeding an old applicationinfo JSON would be incorrect
|
|
277
|
+
if (eventStr && event.jsonName !== APPLICATION_INFO_JSON_NAME) {
|
|
278
|
+
hc.getInstance().registerEventDrop();
|
|
279
|
+
queue.enq(eventStr);
|
|
280
|
+
}
|
|
281
|
+
process.env.WS_HEALTH = 'Error';
|
|
282
|
+
// If the reconnection is not already in progress
|
|
283
|
+
// we would want to reconnect to IC
|
|
284
|
+
// self.reconnect();
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* This is the websocket on open default listener
|
|
289
|
+
* generator.
|
|
290
|
+
*
|
|
291
|
+
* If we fail to ping the ws server, we
|
|
292
|
+
* will retry sending the init ping after a
|
|
293
|
+
* defined delay.
|
|
294
|
+
* Otherwise if the ping is successful we will
|
|
295
|
+
* dispatch the application info.
|
|
296
|
+
*
|
|
297
|
+
* @param {SecWebSocket} self
|
|
298
|
+
*/
|
|
299
|
+
const defaultOnOpenCB = () => async function onOpen() {
|
|
300
|
+
const self = this;
|
|
301
|
+
if (self.isICReadyForEvents) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (self.instance.readyState !== WebSocket.OPEN) {
|
|
305
|
+
self.reconnect();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (pingIntervalConst) {
|
|
309
|
+
clearInterval(pingIntervalConst);
|
|
310
|
+
}
|
|
311
|
+
pingIntervalConst = setInterval(() => {
|
|
312
|
+
self.pingWS();
|
|
313
|
+
}, 30000)
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
initLogger.info(LOG_MESSAGES.WS_CONNECTED);
|
|
317
|
+
|
|
318
|
+
this.wsRetryCount = 0;
|
|
319
|
+
process.env.WS_HEALTH = 'OK';
|
|
320
|
+
|
|
321
|
+
const applicationInfo = APP_INFO || Agent.getAgent().applicationInfo;
|
|
322
|
+
initLogger.info(LOG_MESSAGES.SENDING_APPINFO, JSON.stringify(applicationInfo));
|
|
323
|
+
await self.dispatchApplicationInfo(applicationInfo);
|
|
324
|
+
|
|
325
|
+
// by now we expect IC to be ready
|
|
326
|
+
aquireWebSocket(true);
|
|
327
|
+
self.setICReadyForEvents();
|
|
328
|
+
// we would also want the event queue to be flushed
|
|
329
|
+
await self.flushEventQueue();
|
|
330
|
+
ResponseHandler.FuzzRequestHandler.setLastFuzzEnventTime();
|
|
331
|
+
releaseWebSocket();
|
|
332
|
+
initLogger.info(LOG_MESSAGES.SENDING_APPINFO_COMPLETE, JSON.stringify(applicationInfo));
|
|
333
|
+
} catch (err) {
|
|
334
|
+
logger.error(`Connection broken: ${err.message}`);
|
|
335
|
+
statusUtils.addErrortoBuffer(err);
|
|
336
|
+
process.env.WS_HEALTH = 'Error';
|
|
337
|
+
setWebSocketConn();
|
|
338
|
+
self.setValidatorNotReadyForEvents();
|
|
339
|
+
setTimeout(self.openCB, INIT_PING_RETRY_DELAY_ON_FAIL);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* This is the websocket on error default listener
|
|
345
|
+
* generator.
|
|
346
|
+
*
|
|
347
|
+
* This will close and re-establish the websocket
|
|
348
|
+
* connection once if invoked.
|
|
349
|
+
*
|
|
350
|
+
* @param {SecWebSocket} self
|
|
351
|
+
*/
|
|
352
|
+
const defaultOnMsgCB = () => function onMessage(data) {
|
|
353
|
+
const json = JSON.parse(data);
|
|
354
|
+
ResponseHandler.handle(json);
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* This is the websocket on error default listener
|
|
359
|
+
* generator.
|
|
360
|
+
*
|
|
361
|
+
* This will close and re-establish the websocket
|
|
362
|
+
* connection once if invoked.
|
|
363
|
+
*
|
|
364
|
+
* @param {SecWebSocket} self
|
|
365
|
+
*/
|
|
366
|
+
const defaultOnErrCB = (error) => function onClose(error) {
|
|
367
|
+
const self = this;
|
|
368
|
+
if (error && error.code && (error.code === 'ECONNREFUSED' || error.code === 'EPIPE' || error.code === 'ECONNRESET')) {
|
|
369
|
+
logger.debug("WS Client Error callback called:", error);
|
|
370
|
+
self.obeyReconnect();
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
aquireWebSocket(true);
|
|
374
|
+
self.reconnect();
|
|
375
|
+
}
|
|
376
|
+
statusUtils.addErrortoBuffer(new Error('Error connecting to websocket'));
|
|
377
|
+
process.env.WS_HEALTH = 'Error';
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
SecWebSocket.Events = {
|
|
381
|
+
BUSY,
|
|
382
|
+
IDLE,
|
|
383
|
+
CONNECTING
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
SecWebSocket.States = {
|
|
387
|
+
BUSY,
|
|
388
|
+
IDLE,
|
|
389
|
+
CONNECTING
|
|
390
|
+
};
|
|
391
|
+
SecWebSocket.PING_MSG = ACK;
|
|
392
|
+
SecWebSocket.PING_RETRY_INTERVAL = INIT_PING_RETRY_DELAY_ON_FAIL;
|
|
393
|
+
SecWebSocket.RECONNECTION_DELAY = RECONNECTION_DELAY;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Checks if the websocket is currently busy
|
|
397
|
+
* by checking on CSEC_WS_STATE
|
|
398
|
+
*
|
|
399
|
+
* @returns {boolean} busy
|
|
400
|
+
*/
|
|
401
|
+
function isWebSocketBusy() {
|
|
402
|
+
return process.env.CSEC_WS_STATE !== IDLE;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Sets the websocket state as connecting
|
|
407
|
+
*/
|
|
408
|
+
function setWebSocketConn() {
|
|
409
|
+
process.env.CSEC_WS_ACQUIRED_COUNT = 0;
|
|
410
|
+
process.env.CSEC_WS_STATE = CONNECTING;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* marks the WS being aquired to be used in an operation
|
|
415
|
+
*/
|
|
416
|
+
function aquireWebSocket(forced = false) {
|
|
417
|
+
if (!forced && process.env.CSEC_WS_STATE === CONNECTING) return;
|
|
418
|
+
|
|
419
|
+
let count = Number.parseInt(process.env.CSEC_WS_ACQUIRED_COUNT);
|
|
420
|
+
count += 1;
|
|
421
|
+
process.env.CSEC_WS_ACQUIRED_COUNT = count;
|
|
422
|
+
process.env.CSEC_WS_STATE = BUSY;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* marks the WS being released after use in an operation
|
|
427
|
+
*/
|
|
428
|
+
function releaseWebSocket() {
|
|
429
|
+
const { CSEC_WS_STATE, CSEC_WS_ACQUIRED_COUNT } = process.env;
|
|
430
|
+
let count = Number.parseInt(CSEC_WS_ACQUIRED_COUNT);
|
|
431
|
+
count = count > 0 ? count - 1 : 0;
|
|
432
|
+
process.env.CSEC_WS_ACQUIRED_COUNT = count;
|
|
433
|
+
process.env.CSEC_WS_STATE = count === 0 && CSEC_WS_STATE !== CONNECTING ? CSEC_WS_STATE : IDLE;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
module.exports = {
|
|
437
|
+
WSCallbacks,
|
|
438
|
+
SecWebSocket,
|
|
439
|
+
isWebSocketBusy,
|
|
440
|
+
setWebSocketConn,
|
|
441
|
+
aquireWebSocket,
|
|
442
|
+
releaseWebSocket
|
|
443
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
const { JSON_NAME, COLLECTOR_TYPE, LANGUAGE, EMPTY_STR, STRING, NR_CSEC_FUZZ_REQUEST_ID } = require('./sec-agent-constants');
|
|
8
|
+
const { AGENT_VERSION } = require('../../resources/config');
|
|
9
|
+
const policyManager = require('./Policy');
|
|
10
|
+
const LinkingMetaData = require('./LinkingMetadata');
|
|
11
|
+
const API = require('../../../nr-security-api');
|
|
12
|
+
const NRAgent = API.getNRAgent();
|
|
13
|
+
const lodash = require('lodash');
|
|
14
|
+
const commonUtils = require('./commonUtils');
|
|
15
|
+
/**
|
|
16
|
+
* BasicInfo Structure, which is applicatble to each
|
|
17
|
+
* Event generated from agent.
|
|
18
|
+
*/
|
|
19
|
+
function BasicInfo() {
|
|
20
|
+
this.jsonName = undefined;
|
|
21
|
+
this.pid = undefined;
|
|
22
|
+
this.applicationUUID = undefined;
|
|
23
|
+
this.timestamp = Date.now();
|
|
24
|
+
this.jsonVersion = AGENT_VERSION;
|
|
25
|
+
this.collectorType = COLLECTOR_TYPE;
|
|
26
|
+
this.language = LANGUAGE;
|
|
27
|
+
this.framework = EMPTY_STR;
|
|
28
|
+
this.groupName = commonUtils.getCSECmode();
|
|
29
|
+
this.policyVersion = (!lodash.isEmpty(policyManager.getInstance().data)) ? policyManager.getInstance().data.version : 'DEFAULT';
|
|
30
|
+
|
|
31
|
+
if (NRAgent && NRAgent.config) {
|
|
32
|
+
this.entityGuid = NRAgent.config.entity_guid;
|
|
33
|
+
this.linkingMetadata = LinkingMetaData.getLinkingMetadata();
|
|
34
|
+
this.linkingMetadata.agentRunId = NRAgent.config.run_id;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new Event Object from the passed info.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} fileName
|
|
42
|
+
* @param {string} funcName
|
|
43
|
+
* @param {number} lineNumber
|
|
44
|
+
* @param {*} argument
|
|
45
|
+
* @param {number} eid
|
|
46
|
+
* @param {ApplicationInfo} applicationInfo
|
|
47
|
+
* @param {Object} httpRequest
|
|
48
|
+
* @param {string} jsonName
|
|
49
|
+
* @param {string} eventName
|
|
50
|
+
* @param {Object} source
|
|
51
|
+
*
|
|
52
|
+
* @returns {SecEvent} event
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
function SecEvent(fileName, funcName, lineNumber, argument, eid, applicationInfo, httpRequest, eventName, source, eventCategory, metaData, stkTrace, apiId, isAPIBlocked) {
|
|
56
|
+
BasicInfo.call(this);
|
|
57
|
+
const policy = policyManager.getInstance();
|
|
58
|
+
if (argument) {
|
|
59
|
+
argument = argument instanceof Array ? argument : [argument];
|
|
60
|
+
} else if (typeof argument === STRING) {
|
|
61
|
+
argument = [argument];
|
|
62
|
+
} else {
|
|
63
|
+
argument = null;
|
|
64
|
+
}
|
|
65
|
+
this.jsonVersion = applicationInfo.jsonVersion ? applicationInfo.jsonVersion : null;
|
|
66
|
+
this.collectorVersion = applicationInfo.collectorVersion ? applicationInfo.collectorVersion : null;
|
|
67
|
+
this.buildNumber = applicationInfo.buildNumber ? applicationInfo.buildNumber : null;
|
|
68
|
+
this.source = source || null;
|
|
69
|
+
this.applicationUUID = applicationInfo.applicationUUID ? applicationInfo.applicationUUID : null;
|
|
70
|
+
this.jsonName = JSON_NAME.EVENT;
|
|
71
|
+
this.userFileName = fileName || null;
|
|
72
|
+
this.userMethodName = funcName || null;
|
|
73
|
+
try {
|
|
74
|
+
lineNumber = parseInt(lineNumber);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
}
|
|
77
|
+
this.lineNumber = lineNumber || -1;
|
|
78
|
+
this.parameters = argument;
|
|
79
|
+
this.eventGenerationTime = new Date().getTime();
|
|
80
|
+
this.eid = eid || null;
|
|
81
|
+
this.pid = applicationInfo.pid ? applicationInfo.pid : null;
|
|
82
|
+
this.httpRequest = httpRequest || null;
|
|
83
|
+
this.caseType = eventName || null;
|
|
84
|
+
this.eventCategory = eventCategory || null;
|
|
85
|
+
this.metaData = metaData || {
|
|
86
|
+
TRIGGER_VIA_RCI: false,
|
|
87
|
+
RCI_METHOD_CALLS: []
|
|
88
|
+
};
|
|
89
|
+
this.stacktrace = stkTrace;
|
|
90
|
+
this.apiId = apiId || null;
|
|
91
|
+
this.isAPIBlocked = isAPIBlocked || false;
|
|
92
|
+
const vulnerabilityScan = policy.data.vulnerabilityScan;
|
|
93
|
+
const dynamicScanning = vulnerabilityScan.enabled && vulnerabilityScan.iastScan.enabled;
|
|
94
|
+
this.isIASTEnable = dynamicScanning;
|
|
95
|
+
if (this.isIASTEnable) {
|
|
96
|
+
this.id = eid + Math.floor(Math.random() * (100000 - 1) + 1);
|
|
97
|
+
} else {
|
|
98
|
+
this.id = eid;
|
|
99
|
+
}
|
|
100
|
+
if (httpRequest && httpRequest.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
101
|
+
this.isIASTRequest = true;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.isIASTRequest = false;
|
|
105
|
+
}
|
|
106
|
+
this.eventType = 'sec_event';
|
|
107
|
+
if (NRAgent) {
|
|
108
|
+
const nrTransaction = NRAgent.getTransaction();
|
|
109
|
+
if (nrTransaction) {
|
|
110
|
+
this.linkingMetadata.transactionId = nrTransaction.id;
|
|
111
|
+
const segment = NRAgent.tracer.getSegment();
|
|
112
|
+
if (segment) {
|
|
113
|
+
this.linkingMetadata['span.id'] = segment.getSpanId();
|
|
114
|
+
}
|
|
115
|
+
this.linkingMetadata.isSampled = nrTransaction.isSampled();
|
|
116
|
+
this.linkingMetadata['trace.id'] = nrTransaction.traceId;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
SecEvent.prototype = Object.create(BasicInfo.prototype);
|
|
122
|
+
SecEvent.prototype.constructor = SecEvent;
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
BasicInfo,
|
|
126
|
+
SecEvent: SecEvent,
|
|
127
|
+
EVENT_TYPE: {
|
|
128
|
+
SYSTEM_COMMAND: 'SYSTEM_COMMAND',
|
|
129
|
+
SYSTEM_EXIT: 'SYSTEM_EXIT',
|
|
130
|
+
FILE_OPERATION: 'FILE_OPERATION',
|
|
131
|
+
FILE_INTEGRITY: 'FILE_INTEGRITY',
|
|
132
|
+
DB_COMMAND: 'SQL_DB_COMMAND',
|
|
133
|
+
NOSQL_DB_COMMAND: 'NOSQL_DB_COMMAND',
|
|
134
|
+
HTTP_REQUEST: 'HTTP_REQUEST',
|
|
135
|
+
CODE_INJECTION: 'CODE_INJECTION',
|
|
136
|
+
XXE: 'XXE',
|
|
137
|
+
CIPHER: 'CIPHER',
|
|
138
|
+
HASH: 'HASH',
|
|
139
|
+
RANDOM: 'RANDOM',
|
|
140
|
+
UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
|
|
141
|
+
REFLECTED_XSS: 'REFLECTED_XSS',
|
|
142
|
+
XPATH: 'XPATH',
|
|
143
|
+
LDAP: 'LDAP'
|
|
144
|
+
|
|
145
|
+
},
|
|
146
|
+
EVENT_CATEGORY: {
|
|
147
|
+
MYSQL: 'MYSQL',
|
|
148
|
+
POSTGRES: 'POSTGRES',
|
|
149
|
+
ORACLE: 'ORACLE',
|
|
150
|
+
MONGO: 'MONGO',
|
|
151
|
+
MSSQL: 'MSSQL',
|
|
152
|
+
SQLITE: 'SQLITE',
|
|
153
|
+
FILE: 'FILE',
|
|
154
|
+
SYS: 'SYSTEM',
|
|
155
|
+
HTTP: 'HTTP',
|
|
156
|
+
CODE_INJECTION: 'CODE_INJECTION',
|
|
157
|
+
XXE: 'XXE',
|
|
158
|
+
CRYPTO: 'CRYPTO',
|
|
159
|
+
HASH: 'HASH',
|
|
160
|
+
WEAKRANDOM: 'WEAKRANDOM',
|
|
161
|
+
UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
|
|
162
|
+
REFLECTED_XSS: 'REFLECTED_XSS',
|
|
163
|
+
XPATH: 'XPATH',
|
|
164
|
+
LDAP: 'LDAP'
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
VUNERABILITIES:
|
|
168
|
+
{
|
|
169
|
+
SQLI: 'SQLI',
|
|
170
|
+
SXSS: 'SXSS',
|
|
171
|
+
RXSS: 'RXSS',
|
|
172
|
+
LDAP: 'LDAP',
|
|
173
|
+
RCE: 'RCE',
|
|
174
|
+
RCI: 'RCI',
|
|
175
|
+
FILE_ACCESS: 'FILE_ACCESS',
|
|
176
|
+
NOSQLI: 'NOSQLI',
|
|
177
|
+
WEAK_RANDOM: 'WEAK_RANDOM',
|
|
178
|
+
XPATH: 'XPATH',
|
|
179
|
+
SSRF: 'SSRF',
|
|
180
|
+
CRYPTO: 'CRYPTO',
|
|
181
|
+
HASH: 'HASH',
|
|
182
|
+
XXE: 'XXE',
|
|
183
|
+
UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
};
|