@newrelic/security-agent 0.1.3 → 0.2.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 +16 -0
- package/THIRD_PARTY_NOTICES.md +3 -3
- package/lib/instrumentation-security/core/constants.js +0 -1
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +6 -5
- package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +16 -0
- package/lib/instrumentation-security/hooks/require/nr-require.js +12 -5
- package/lib/nr-security-agent/lib/core/Auth-headers.js +1 -0
- package/lib/nr-security-agent/lib/core/applicationinfo.js +1 -1
- package/lib/nr-security-agent/lib/core/commonUtils.js +10 -10
- package/lib/nr-security-agent/lib/core/connections/websocket/index.js +0 -6
- package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +50 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/collector-policy-handler.js +4 -1
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +90 -31
- package/lib/nr-security-agent/lib/core/connections/websocket/response/iast-cool-down.js +15 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/iast-record-deletion.js +19 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/index.js +17 -5
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +9 -58
- package/lib/nr-security-agent/lib/core/logging.js +89 -71
- package/lib/nr-security-agent/lib/core/sec-agent-constants.js +3 -2
- package/lib/nr-security-agent/lib/core/statusUtils.js +1 -1
- package/lib/nr-security-agent/lib/core/websocket-client.js +5 -168
- package/package.json +4 -4
- package/lib/nr-security-agent/lib/core/worker-entities.js +0 -82
- package/lib/nr-security-agent/lib/core/worker.js +0 -148
|
@@ -32,8 +32,9 @@ const INIT_PING_RETRY_DELAY_ON_FAIL = 200; // in ms
|
|
|
32
32
|
const RECONNECTION_DELAY = 500; // ms
|
|
33
33
|
const APPLICATION_INFO_JSON_NAME = 'applicationinfo';
|
|
34
34
|
let APP_INFO;
|
|
35
|
+
let CSEC_WS_STATE = '';
|
|
35
36
|
|
|
36
|
-
const queue = new RingBuffer(
|
|
37
|
+
const queue = new RingBuffer(10);
|
|
37
38
|
let logger = logs.getLogger();
|
|
38
39
|
const initLogger = logs.getInitLogger();
|
|
39
40
|
let webSocket;
|
|
@@ -119,7 +120,6 @@ SecWebSocket.prototype.init = function init() {
|
|
|
119
120
|
logger.debug("Pong received from Validator");
|
|
120
121
|
})
|
|
121
122
|
webSocket.on('close', () => {
|
|
122
|
-
commonUtils.setWSHealthStatus('Error');
|
|
123
123
|
logger.warn("WS connection closed");
|
|
124
124
|
})
|
|
125
125
|
this.instance = webSocket;
|
|
@@ -132,7 +132,7 @@ SecWebSocket.prototype.init = function init() {
|
|
|
132
132
|
SecWebSocket.prototype.reconnect = function reconnect() {
|
|
133
133
|
const { instance } = this;
|
|
134
134
|
if (instance.readyState === WebSocket.CONNECTING &&
|
|
135
|
-
|
|
135
|
+
CSEC_WS_STATE === CONNECTING) {
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
setWebSocketConn();
|
|
@@ -207,19 +207,15 @@ SecWebSocket.prototype.dispatch = async function dispatch(event) {
|
|
|
207
207
|
!this.isICReadyForEvents
|
|
208
208
|
) {
|
|
209
209
|
handleDispatchFailure(this, event, eventStr);
|
|
210
|
-
commonUtils.setWSHealthStatus('Error')
|
|
211
210
|
return;
|
|
212
211
|
}
|
|
213
212
|
try {
|
|
214
|
-
aquireWebSocket();
|
|
215
213
|
await promisify(this.instance, this.instance.send)(eventStr, { mask: true }, () => {
|
|
216
214
|
logger.debug(LOG_MESSAGES.EVENT_SENT + eventStr);
|
|
217
215
|
});
|
|
218
|
-
releaseWebSocket();
|
|
219
216
|
} catch (error) {
|
|
220
217
|
logger.debug(LOG_MESSAGES.ERROR_WHILE_SEND_EVENT, eventStr, error);
|
|
221
218
|
statusUtils.addErrortoBuffer(error);
|
|
222
|
-
commonUtils.setWSHealthStatus('Error')
|
|
223
219
|
handleDispatchFailure(this, event, eventStr);
|
|
224
220
|
}
|
|
225
221
|
};
|
|
@@ -269,7 +265,6 @@ SecWebSocket.prototype.setValidatorNotReadyForEvents = function setValidatorNotR
|
|
|
269
265
|
* recover these events.
|
|
270
266
|
*/
|
|
271
267
|
SecWebSocket.prototype.flushEventQueue = async function flushEventQueue() {
|
|
272
|
-
aquireWebSocket();
|
|
273
268
|
try {
|
|
274
269
|
const sentEvents = [];
|
|
275
270
|
for (let counter = 0; counter < queue.size(); counter += 1) {
|
|
@@ -280,9 +275,7 @@ SecWebSocket.prototype.flushEventQueue = async function flushEventQueue() {
|
|
|
280
275
|
hc.getInstance().registerEventsSent(results.length);
|
|
281
276
|
} catch (err) {
|
|
282
277
|
statusUtils.addErrortoBuffer(err);
|
|
283
|
-
logger.
|
|
284
|
-
} finally {
|
|
285
|
-
releaseWebSocket();
|
|
278
|
+
logger.debug('Flush failed, reception of all events on IC couldn\'t be ensured.');
|
|
286
279
|
}
|
|
287
280
|
};
|
|
288
281
|
|
|
@@ -298,7 +291,6 @@ const handleDispatchFailure = (self, event, eventStr) => {
|
|
|
298
291
|
// so feeding an old applicationinfo JSON would be incorrect
|
|
299
292
|
if (eventStr && event.jsonName !== APPLICATION_INFO_JSON_NAME) {
|
|
300
293
|
hc.getInstance().registerEventDrop();
|
|
301
|
-
queue.enq(eventStr);
|
|
302
294
|
}
|
|
303
295
|
};
|
|
304
296
|
|
|
@@ -332,7 +324,7 @@ const defaultOnOpenCB = () => async function onOpen() {
|
|
|
332
324
|
self.reconnect();
|
|
333
325
|
return;
|
|
334
326
|
}
|
|
335
|
-
|
|
327
|
+
|
|
336
328
|
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
337
329
|
initLogger.info(`[STEP-4] => Web socket connection to SaaS validator established successfully at ${validatorService}`)
|
|
338
330
|
try {
|
|
@@ -343,13 +335,8 @@ const defaultOnOpenCB = () => async function onOpen() {
|
|
|
343
335
|
const applicationInfo = APP_INFO || Agent.getAgent().applicationInfo;
|
|
344
336
|
await self.dispatchApplicationInfo(applicationInfo);
|
|
345
337
|
|
|
346
|
-
// by now we expect IC to be ready
|
|
347
|
-
aquireWebSocket(true);
|
|
348
338
|
self.setICReadyForEvents();
|
|
349
|
-
|
|
350
|
-
await self.flushEventQueue();
|
|
351
|
-
ResponseHandler.FuzzRequestHandler.setLastFuzzEnventTime();
|
|
352
|
-
releaseWebSocket();
|
|
339
|
+
ResponseHandler.FuzzRequestHandler.setLastFuzzEventTime();
|
|
353
340
|
initLogger.info(LOG_MESSAGES.SENDING_APPINFO_COMPLETE, JSON.stringify(applicationInfo));
|
|
354
341
|
} catch (err) {
|
|
355
342
|
logger.error(`Connection broken: ${err.message}`);
|
|
@@ -388,14 +375,13 @@ const defaultOnErrCB = () => function onClose(error) {
|
|
|
388
375
|
commonUtils.setWSHealthStatus('Error');
|
|
389
376
|
statusUtils.addErrortoBuffer(error);
|
|
390
377
|
const self = this;
|
|
391
|
-
logger.error("Error while connecting to validator:",error.message)
|
|
378
|
+
logger.error("Error while connecting to validator:", error.message)
|
|
392
379
|
logger.warn("Security Agent INACTIVE!!!")
|
|
393
380
|
if (error && error.code && (error.code === 'ECONNREFUSED' || error.code === 'EPIPE' || error.code === 'ECONNRESET')) {
|
|
394
381
|
logger.debug("WS Client Error callback called:", error);
|
|
395
382
|
self.obeyReconnect();
|
|
396
383
|
}
|
|
397
384
|
else {
|
|
398
|
-
aquireWebSocket(true);
|
|
399
385
|
self.reconnect();
|
|
400
386
|
}
|
|
401
387
|
};
|
|
@@ -415,52 +401,17 @@ SecWebSocket.PING_MSG = ACK;
|
|
|
415
401
|
SecWebSocket.PING_RETRY_INTERVAL = INIT_PING_RETRY_DELAY_ON_FAIL;
|
|
416
402
|
SecWebSocket.RECONNECTION_DELAY = RECONNECTION_DELAY;
|
|
417
403
|
|
|
418
|
-
/**
|
|
419
|
-
* Checks if the websocket is currently busy
|
|
420
|
-
* by checking on CSEC_WS_STATE
|
|
421
|
-
*
|
|
422
|
-
* @returns {boolean} busy
|
|
423
|
-
*/
|
|
424
|
-
function isWebSocketBusy() {
|
|
425
|
-
return process.env.CSEC_WS_STATE !== IDLE;
|
|
426
|
-
}
|
|
427
404
|
|
|
428
405
|
/**
|
|
429
406
|
* Sets the websocket state as connecting
|
|
430
407
|
*/
|
|
431
408
|
function setWebSocketConn() {
|
|
432
|
-
|
|
433
|
-
process.env.CSEC_WS_STATE = CONNECTING;
|
|
409
|
+
CSEC_WS_STATE = CONNECTING;
|
|
434
410
|
}
|
|
435
411
|
|
|
436
|
-
/**
|
|
437
|
-
* marks the WS being aquired to be used in an operation
|
|
438
|
-
*/
|
|
439
|
-
function aquireWebSocket(forced = false) {
|
|
440
|
-
if (!forced && process.env.CSEC_WS_STATE === CONNECTING) return;
|
|
441
|
-
|
|
442
|
-
let count = Number.parseInt(process.env.CSEC_WS_ACQUIRED_COUNT);
|
|
443
|
-
count += 1;
|
|
444
|
-
process.env.CSEC_WS_ACQUIRED_COUNT = count;
|
|
445
|
-
process.env.CSEC_WS_STATE = BUSY;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* marks the WS being released after use in an operation
|
|
450
|
-
*/
|
|
451
|
-
function releaseWebSocket() {
|
|
452
|
-
const { CSEC_WS_STATE, CSEC_WS_ACQUIRED_COUNT } = process.env;
|
|
453
|
-
let count = Number.parseInt(CSEC_WS_ACQUIRED_COUNT);
|
|
454
|
-
count = count > 0 ? count - 1 : 0;
|
|
455
|
-
process.env.CSEC_WS_ACQUIRED_COUNT = count;
|
|
456
|
-
process.env.CSEC_WS_STATE = count === 0 && CSEC_WS_STATE !== CONNECTING ? CSEC_WS_STATE : IDLE;
|
|
457
|
-
}
|
|
458
412
|
|
|
459
413
|
module.exports = {
|
|
460
414
|
WSCallbacks,
|
|
461
415
|
SecWebSocket,
|
|
462
|
-
|
|
463
|
-
setWebSocketConn,
|
|
464
|
-
aquireWebSocket,
|
|
465
|
-
releaseWebSocket
|
|
416
|
+
setWebSocketConn
|
|
466
417
|
};
|
|
@@ -16,18 +16,24 @@ const LOG_PATTERN_OBJ = {
|
|
|
16
16
|
type: 'pattern',
|
|
17
17
|
pattern: '%[%d[%p][%c][%h][%z]%] %m'
|
|
18
18
|
};
|
|
19
|
-
const { LOG_DIR } = require('./sec-agent-constants');
|
|
19
|
+
const { LOG_DIR, CSEC_HOME } = require('./sec-agent-constants');
|
|
20
20
|
let logger;
|
|
21
21
|
const API = require('../../../nr-security-api');
|
|
22
22
|
const NRAgent = API.getNRAgent();
|
|
23
23
|
|
|
24
24
|
const logFileName = `${LOG_DIR}node-security-collector.log`;
|
|
25
25
|
const initLogFileName = `${LOG_DIR}node-security-collector-init.log`;
|
|
26
|
+
let isLoggerOk = true;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
function createLogPathIfNotExist(dir) {
|
|
28
30
|
try {
|
|
29
31
|
if (!fs.existsSync(dir)) {
|
|
30
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
fs.mkdirSync(dir, 777, { recursive: true });
|
|
33
|
+
fs.chmodSync(dir, 0o777);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
fs.chmodSync(dir, 0o777);
|
|
31
37
|
}
|
|
32
38
|
} catch (error) {
|
|
33
39
|
}
|
|
@@ -37,90 +43,103 @@ function createLogPathIfNotExist (dir) {
|
|
|
37
43
|
* Initializes the logger appenders.
|
|
38
44
|
* @param {v4uuid} applicationUUID
|
|
39
45
|
*/
|
|
40
|
-
function initialize
|
|
41
|
-
const appenders = {};
|
|
42
|
-
appenders.NR_CSEC = {
|
|
43
|
-
type: 'file',
|
|
44
|
-
compress: false,
|
|
45
|
-
layout: LOG_PATTERN_OBJ
|
|
46
|
-
};
|
|
47
|
-
appenders.NR_CSEC_INIT = {
|
|
48
|
-
type: 'file',
|
|
49
|
-
compress: false,
|
|
50
|
-
layout: LOG_PATTERN_OBJ
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const logLevel = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.level : 'info';
|
|
54
|
-
|
|
46
|
+
function initialize(applicationUUID) {
|
|
55
47
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
const logLevel = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.level : 'info';
|
|
49
|
+
const apmLogFilePath = NRAgent && NRAgent.config && NRAgent.config.logging.enabled ? NRAgent.config.logging.filepath : '';
|
|
50
|
+
let appenderType = 'file';
|
|
51
|
+
|
|
52
|
+
if (apmLogFilePath === 'stdout') {
|
|
53
|
+
appenderType = 'stdout';
|
|
58
54
|
}
|
|
59
|
-
} catch (error) {
|
|
60
|
-
}
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
Object.assign(appenders.NR_CSEC_INIT, {
|
|
69
|
-
filename: initLogFileName,
|
|
70
|
-
maxLogSize: LOG_FILE_SIZE,
|
|
71
|
-
backups: 0
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const categories = {
|
|
75
|
-
default: {
|
|
76
|
-
appenders: [
|
|
77
|
-
'NR_CSEC'
|
|
78
|
-
],
|
|
79
|
-
level: logLevel
|
|
80
|
-
},
|
|
81
|
-
NR_CSEC_INIT: {
|
|
82
|
-
appenders: [
|
|
83
|
-
'NR_CSEC_INIT',
|
|
84
|
-
'NR_CSEC'
|
|
85
|
-
],
|
|
86
|
-
level: logLevel
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
if (env === 'dev' || env === 'trace') {
|
|
91
|
-
appenders.STDOUT = {
|
|
92
|
-
type: 'stdout',
|
|
56
|
+
const appenders = {};
|
|
57
|
+
appenders.NR_CSEC = {
|
|
58
|
+
type: appenderType,
|
|
59
|
+
compress: false,
|
|
93
60
|
layout: LOG_PATTERN_OBJ
|
|
94
61
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
62
|
+
appenders.NR_CSEC_INIT = {
|
|
63
|
+
type: appenderType,
|
|
64
|
+
compress: false,
|
|
65
|
+
layout: LOG_PATTERN_OBJ
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
createLogPathIfNotExist(CSEC_HOME);
|
|
70
|
+
createLogPathIfNotExist(LOG_DIR);
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Object.assign(appenders.NR_CSEC, {
|
|
74
|
+
filename: logFileName,
|
|
75
|
+
maxLogSize: LOG_FILE_SIZE,
|
|
76
|
+
backups: 0
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
Object.assign(appenders.NR_CSEC_INIT, {
|
|
80
|
+
filename: initLogFileName,
|
|
81
|
+
maxLogSize: LOG_FILE_SIZE,
|
|
82
|
+
backups: 0
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const categories = {
|
|
86
|
+
default: {
|
|
87
|
+
appenders: [
|
|
88
|
+
'NR_CSEC'
|
|
89
|
+
],
|
|
90
|
+
level: logLevel
|
|
91
|
+
},
|
|
92
|
+
NR_CSEC_INIT: {
|
|
93
|
+
appenders: [
|
|
94
|
+
'NR_CSEC_INIT',
|
|
95
|
+
],
|
|
96
|
+
level: logLevel
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (apmLogFilePath !== 'stdout') {
|
|
101
|
+
categories.NR_CSEC_INIT.appenders.push('NR_CSEC')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if (env === 'dev' || env === 'trace') {
|
|
106
|
+
appenders.STDOUT = {
|
|
107
|
+
type: 'stdout',
|
|
108
|
+
layout: LOG_PATTERN_OBJ
|
|
109
|
+
};
|
|
110
|
+
categories.default.appenders.push('STDOUT');
|
|
111
|
+
categories.NR_CSEC_INIT.appenders.push('STDOUT');
|
|
112
|
+
}
|
|
98
113
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
log4js.configure({
|
|
115
|
+
appenders: appenders,
|
|
116
|
+
categories: categories,
|
|
117
|
+
disableClustering: true
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error("Error while logging into file. Fallback to stdout.", error.message);
|
|
121
|
+
isLoggerOk = false;
|
|
122
|
+
}
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
/**
|
|
108
126
|
* sets the logging level of current instance.
|
|
109
127
|
* @param {String} level
|
|
110
128
|
*/
|
|
111
|
-
function setLevel
|
|
129
|
+
function setLevel(level = INFO) {
|
|
112
130
|
level = level.trim();
|
|
113
131
|
if (logger) {
|
|
114
132
|
logger.level = level;
|
|
115
|
-
}
|
|
116
|
-
|
|
133
|
+
}
|
|
134
|
+
if (!isLoggerOk) {
|
|
135
|
+
logger.level = INFO;
|
|
117
136
|
}
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
/**
|
|
121
140
|
* get the logging level of current instance.
|
|
122
141
|
*/
|
|
123
|
-
function getLevel
|
|
142
|
+
function getLevel() {
|
|
124
143
|
if (logger) {
|
|
125
144
|
return logger.level;
|
|
126
145
|
} else {
|
|
@@ -134,7 +153,7 @@ function getLevel () {
|
|
|
134
153
|
* already made.
|
|
135
154
|
* @param {v4uuid} applicationUUID
|
|
136
155
|
*/
|
|
137
|
-
function getLogger
|
|
156
|
+
function getLogger(applicationUUID = process.env.applicationUUID) {
|
|
138
157
|
if (logger) {
|
|
139
158
|
return logger;
|
|
140
159
|
}
|
|
@@ -146,9 +165,8 @@ function getLogger (applicationUUID = process.env.applicationUUID) {
|
|
|
146
165
|
fs.chmodSync(`${logFileName}`, 0o777);
|
|
147
166
|
fs.chmodSync(`${initLogFileName}`, 0o777);
|
|
148
167
|
} catch (error) {
|
|
149
|
-
logger.error("Error while setting up permissions on log files", error);
|
|
150
168
|
}
|
|
151
|
-
initLogger.info("[STEP-2] => Generating unique identifier:",applicationUUID
|
|
169
|
+
initLogger.info("[STEP-2] => Generating unique identifier:", applicationUUID)
|
|
152
170
|
if (NRAgent) {
|
|
153
171
|
setLevel(NRAgent.config.logging.level);
|
|
154
172
|
}
|
|
@@ -159,14 +177,14 @@ function getLogger (applicationUUID = process.env.applicationUUID) {
|
|
|
159
177
|
}
|
|
160
178
|
}
|
|
161
179
|
|
|
162
|
-
function reCreateLogger
|
|
180
|
+
function reCreateLogger(applicationUUID) {
|
|
163
181
|
initialize(applicationUUID);
|
|
164
182
|
logger = log4js.getLogger('NR_CSEC');
|
|
165
183
|
setLevel(logger.level);
|
|
166
184
|
updateModuleLoggers(logger);
|
|
167
185
|
}
|
|
168
186
|
|
|
169
|
-
function getInitLogger
|
|
187
|
+
function getInitLogger() {
|
|
170
188
|
const initLogger = log4js.getLogger('NR_CSEC_INIT');
|
|
171
189
|
return initLogger;
|
|
172
190
|
}
|
|
@@ -49,7 +49,9 @@ module.exports = {
|
|
|
49
49
|
COLLECTOR_CONFIG: 100,
|
|
50
50
|
BLOCKING: 101,
|
|
51
51
|
POLICY_ERROR: 102,
|
|
52
|
-
OBEY_WS_RECONNECT:12
|
|
52
|
+
OBEY_WS_RECONNECT:12,
|
|
53
|
+
ENTER_IAST_COOLDOWN:13,
|
|
54
|
+
IAST_RECORD_DELETE_CONFIRMATION:14
|
|
53
55
|
|
|
54
56
|
},
|
|
55
57
|
JSON_NAME: {
|
|
@@ -74,7 +76,6 @@ module.exports = {
|
|
|
74
76
|
(process.env[AWS_EXECUTION_ENV_VAR] || '').match(AWS_LAMBDA_NODE_EXEC_ENV_IDENTIFIER)
|
|
75
77
|
),
|
|
76
78
|
LAMBDA_RUNTIME_DIR_VAR: 'LAMBDA_RUNTIME_DIR',
|
|
77
|
-
CSEC_LAST_FUZZ_REQUEST_TIME: 'CSEC_LAST_FUZZ_REQUEST_TIME',
|
|
78
79
|
NR_CSEC_FUZZ_REQUEST_ID: 'nr-csec-fuzz-request-id',
|
|
79
80
|
EMPTY_APPLICATION_UUID: '--0--',
|
|
80
81
|
QUESTIONMARK: '?',
|
|
@@ -95,7 +95,7 @@ function writeSnapshot() {
|
|
|
95
95
|
commonUtils.removeOlderSnapshots();
|
|
96
96
|
fs.writeFile(statusFile, snapshot, { mode: 0o777 }, function (err) {
|
|
97
97
|
if (err) {
|
|
98
|
-
logger.
|
|
98
|
+
logger.debug(err.message);
|
|
99
99
|
} else {
|
|
100
100
|
logger.info('Snapshot updated to file: %s', statusFile);
|
|
101
101
|
fs.chmod(statusFile, 0o777, (err) => {
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const RingBuffer = require('ringbufferjs');
|
|
9
7
|
const LOG_MESSAGES = require('./sec-agent-constants').LOG_MESSAGES;
|
|
10
8
|
|
|
11
9
|
const { Agent } = require('./agent');
|
|
@@ -14,31 +12,21 @@ const hc = require('./health-check');
|
|
|
14
12
|
const applicationInfo = require('./applicationinfo').getInstance();
|
|
15
13
|
const logger = require('./logging').getLogger(applicationInfo.applicationUUID);
|
|
16
14
|
const initLogger = require('./logging').getInitLogger();
|
|
17
|
-
const { WorkerMessage, WORKER_STATUS } = require('./worker-entities');
|
|
18
15
|
const commonUtils = require('./commonUtils');
|
|
19
16
|
const API = require('../../../nr-security-api');
|
|
20
17
|
const NRAgent = API.getNRAgent();
|
|
21
18
|
const {
|
|
22
19
|
SecWebSocket,
|
|
23
20
|
setWebSocketConn,
|
|
24
|
-
aquireWebSocket,
|
|
25
21
|
ResponseHandler: {
|
|
26
|
-
handle: handleServerResponse,
|
|
27
22
|
setLogger: setResponseHandlerLogger
|
|
28
23
|
}
|
|
29
24
|
} = require('./connections/websocket');
|
|
30
25
|
const { HC_INTERVAL_MS } = require('../../resources/config');
|
|
31
|
-
const {
|
|
26
|
+
const { LOG_DIR } = require('./sec-agent-constants');
|
|
32
27
|
const statusUtils = require('./statusUtils');
|
|
33
28
|
const njsAgentConstants = require('./sec-agent-constants');
|
|
34
29
|
|
|
35
|
-
const MAX_BUSY_WAIT_TIME = 5000; // ms
|
|
36
|
-
const workerSupported = commonUtils.hasWorker();;
|
|
37
|
-
const filePath = path.join(__dirname, 'worker.js');
|
|
38
|
-
const localQueue = new RingBuffer(1000);
|
|
39
|
-
|
|
40
|
-
let busy = false;
|
|
41
|
-
let wsWorker;
|
|
42
30
|
let wsInstance;
|
|
43
31
|
let dispatcher;
|
|
44
32
|
|
|
@@ -65,11 +53,8 @@ const initialize = function () {
|
|
|
65
53
|
|
|
66
54
|
setResponseHandlerLogger(logger);
|
|
67
55
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
} else {
|
|
71
|
-
initializeInMainThread();
|
|
72
|
-
}
|
|
56
|
+
initializeInMainThread();
|
|
57
|
+
|
|
73
58
|
// we'll dispatch the application info if it is updated
|
|
74
59
|
Agent.eventEmitter.on(Agent.EVENTS.APPLICATION_INFO_UPDATE_EVENT, (applicationInfo) => {
|
|
75
60
|
if (!NRAgent || (NRAgent && applicationInfo.entityGuid)) {
|
|
@@ -78,12 +63,7 @@ const initialize = function () {
|
|
|
78
63
|
});
|
|
79
64
|
Agent.eventEmitter.on(Agent.EVENTS.RECONNECT_WS_EVENT, () => {
|
|
80
65
|
setWebSocketConn();
|
|
81
|
-
|
|
82
|
-
const reConnectMsg = new WorkerMessage(WorkerMessage.TYPE.RECONNECT, {});
|
|
83
|
-
wsWorker.postMessage(reConnectMsg);
|
|
84
|
-
} else {
|
|
85
|
-
wsInstance.reconnect();
|
|
86
|
-
}
|
|
66
|
+
wsInstance.reconnect();
|
|
87
67
|
});
|
|
88
68
|
|
|
89
69
|
setTimeout(() => {
|
|
@@ -106,102 +86,11 @@ function initializeInMainThread() {
|
|
|
106
86
|
dispatcher = function dispatcher(message) {
|
|
107
87
|
wsInstance.dispatch(message);
|
|
108
88
|
};
|
|
109
|
-
|
|
110
|
-
// Setup the event listeners
|
|
111
|
-
wsInstance.event.on(SecWebSocket.Events.BUSY, function () {
|
|
112
|
-
busy = true;
|
|
113
|
-
});
|
|
114
|
-
wsInstance.event.on(SecWebSocket.Events.INACTIVE, function () {
|
|
115
|
-
busy = false;
|
|
116
|
-
});
|
|
117
89
|
} else {
|
|
118
90
|
return wsInstance;
|
|
119
91
|
}
|
|
120
92
|
}
|
|
121
93
|
|
|
122
|
-
/**
|
|
123
|
-
* This initiates the ws in worker thread
|
|
124
|
-
*/
|
|
125
|
-
function initiateInWorkerThread() {
|
|
126
|
-
if (workerSupported) {
|
|
127
|
-
process.env.WS_WORKER_STATUS = WORKER_STATUS.INITIALIZING;
|
|
128
|
-
try {
|
|
129
|
-
setWebSocketConn();
|
|
130
|
-
const { Worker, SHARE_ENV } = require('worker_threads');
|
|
131
|
-
wsWorker = new Worker(filePath, {
|
|
132
|
-
workerData: {
|
|
133
|
-
applicationInfo
|
|
134
|
-
},
|
|
135
|
-
env: SHARE_ENV
|
|
136
|
-
});
|
|
137
|
-
wsWorker.on('message', handleWorkerMessage);
|
|
138
|
-
wsWorker.on('error', (err) => {
|
|
139
|
-
logger.error('Exception in web socket worker:', err.message);
|
|
140
|
-
wsWorker = new Worker(filePath, {
|
|
141
|
-
workerData: {
|
|
142
|
-
applicationInfo
|
|
143
|
-
}, env: SHARE_ENV
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
dispatcher = function dispatcher(message) {
|
|
147
|
-
if (process.env.WS_WORKER_STATUS !== WORKER_STATUS.RUNNING) {
|
|
148
|
-
localQueue.enq(message);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
// websocket is aquired here and then
|
|
152
|
-
// released in worker thread when the
|
|
153
|
-
// handoff to websocket is confirmed
|
|
154
|
-
aquireWebSocket();
|
|
155
|
-
const msg = new WorkerMessage(WorkerMessage.TYPE.MESSAGE, message);
|
|
156
|
-
wsWorker.postMessage(msg);
|
|
157
|
-
};
|
|
158
|
-
logger.info(LOG_MESSAGES.NODE_VERSION_MSG_WORKER, process.version);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
logger.debug('error is:', error);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* This handles any messages received from worker
|
|
167
|
-
* if websocket worker is initialized
|
|
168
|
-
*
|
|
169
|
-
* @param {JSON} json
|
|
170
|
-
*/
|
|
171
|
-
function handleWorkerMessage(json) {
|
|
172
|
-
const msg = WorkerMessage.parseFromObject(json);
|
|
173
|
-
switch (msg.type) {
|
|
174
|
-
case WorkerMessage.TYPE.MESSAGE: {
|
|
175
|
-
logger.debug(LOG_MESSAGES.MSG_FROM_WORKKER, msg.getPayload());
|
|
176
|
-
handleServerResponse(msg.getPayload());
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case WorkerMessage.TYPE.FLUSH: {
|
|
180
|
-
const { status } = msg.getPayload();
|
|
181
|
-
if (status === 'flushed') {
|
|
182
|
-
logger.debug('Message from worker thread: Flush Completed');
|
|
183
|
-
}
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
case WorkerMessage.TYPE.IC_READY_FOR_EVENTS: {
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
case WorkerMessage.TYPE.BUSY: {
|
|
190
|
-
busy = true;
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
case WorkerMessage.TYPE.INACTIVE: {
|
|
194
|
-
busy = false;
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
case WorkerMessage.TYPE.ERROR: {
|
|
198
|
-
statusUtils.addErrortoBuffer(msg.getPayload());
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
default:
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
94
|
/**
|
|
206
95
|
* Sends Agent HelthCheck to Validator.
|
|
207
96
|
*/
|
|
@@ -267,66 +156,14 @@ function sendEvent(event, callback = DEFAULT_EVENT_SEND_CB) {
|
|
|
267
156
|
callback();
|
|
268
157
|
};
|
|
269
158
|
|
|
270
|
-
/**
|
|
271
|
-
* Flushes local queue to the worker
|
|
272
|
-
*/
|
|
273
|
-
function flushLocalQueue() {
|
|
274
|
-
if (!workerSupported) return;
|
|
275
|
-
|
|
276
|
-
while (!localQueue.isEmpty()) {
|
|
277
|
-
dispatcher(localQueue.deq());
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Waits for websocket to get idel before
|
|
283
|
-
* exiting the lambda function
|
|
284
|
-
* @param {Number} startTime
|
|
285
|
-
*/
|
|
286
|
-
function busyWaitOnWorkerThread(startTime = Date.now()) {
|
|
287
|
-
if (!workerSupported) return;
|
|
288
|
-
|
|
289
|
-
const workerInitiatedOnWait = process.env.WS_WORKER_STATUS === WORKER_STATUS.RUNNING;
|
|
290
|
-
const enableWait = !IS_LAMBDA_ENV || process.env[LAMBDA_ENABLE_RESPONSE_BLOCKING] === 'true';
|
|
291
|
-
|
|
292
|
-
let flushed = false;
|
|
293
|
-
let wait = (
|
|
294
|
-
process.env.CSEC_WS_STATE === SecWebSocket.States.CONNECTING ||
|
|
295
|
-
(
|
|
296
|
-
enableWait &&
|
|
297
|
-
process.env.CSEC_WS_STATE === SecWebSocket.States.BUSY
|
|
298
|
-
)
|
|
299
|
-
) && (startTime + MAX_BUSY_WAIT_TIME > Date.now());
|
|
300
|
-
while (wait) {
|
|
301
|
-
wait = (
|
|
302
|
-
process.env.CSEC_WS_STATE === SecWebSocket.States.CONNECTING ||
|
|
303
|
-
(
|
|
304
|
-
enableWait &&
|
|
305
|
-
process.env.CSEC_WS_STATE === SecWebSocket.States.BUSY
|
|
306
|
-
)
|
|
307
|
-
) && (startTime + MAX_BUSY_WAIT_TIME > Date.now());
|
|
308
|
-
|
|
309
|
-
if (!flushed && !workerInitiatedOnWait && process.env.WS_WORKER_STATUS === WORKER_STATUS.RUNNING) {
|
|
310
|
-
flushed = true;
|
|
311
|
-
flushLocalQueue();
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
159
|
|
|
316
160
|
function obeyReconnect() {
|
|
317
|
-
|
|
318
|
-
const reConnectMsg = new WorkerMessage(WorkerMessage.TYPE.OBEY_RECONNECT, {});
|
|
319
|
-
wsWorker.postMessage(reConnectMsg);
|
|
320
|
-
} else {
|
|
321
|
-
wsInstance.obeyReconnect();
|
|
322
|
-
}
|
|
161
|
+
wsInstance.obeyReconnect();
|
|
323
162
|
}
|
|
324
163
|
|
|
325
164
|
module.exports = {
|
|
326
165
|
initialize,
|
|
327
166
|
write: sendEvent,
|
|
328
167
|
dispatcher: getDispatcherAndSendEvent,
|
|
329
|
-
flushLocalQueue,
|
|
330
|
-
busyWaitOnWorkerThread,
|
|
331
168
|
obeyReconnect
|
|
332
169
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/security-agent",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "New Relic Security Agent for Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"jsonVersion": "1.0.1",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"url": "https://github.com/newrelic/csec-node-agent.git"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@aws-sdk/client-lambda": "^3.
|
|
38
|
+
"@aws-sdk/client-lambda": "^3.363.0",
|
|
39
39
|
"axios": "0.21.4",
|
|
40
|
-
"check-disk-space": "
|
|
40
|
+
"check-disk-space": "3.3.1",
|
|
41
41
|
"content-type": "^1.0.5",
|
|
42
42
|
"fast-safe-stringify": "^2.1.1",
|
|
43
43
|
"find-package-json": "^1.2.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"pretty-bytes": "^5.6.0",
|
|
52
52
|
"request-ip": "^3.3.0",
|
|
53
53
|
"ringbufferjs": "^2.0.0",
|
|
54
|
-
"semver": "^7.5.
|
|
54
|
+
"semver": "^7.5.4",
|
|
55
55
|
"sync-request": "^6.1.0",
|
|
56
56
|
"unescape": "^1.0.1",
|
|
57
57
|
"unescape-js": "^1.1.4",
|