@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,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
let startupConfig = undefined;
|
|
7
|
+
function StartupConfig(){
|
|
8
|
+
|
|
9
|
+
}
|
|
10
|
+
StartupConfig.prototype.constructor = StartupConfig;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns the current instanceof CollectorConfig,
|
|
14
|
+
* creates one if not already created.
|
|
15
|
+
*
|
|
16
|
+
* @returns {StartupConfig} instance
|
|
17
|
+
*/
|
|
18
|
+
function getInstance() {
|
|
19
|
+
if (!startupConfig) {
|
|
20
|
+
startupConfig = new StartupConfig();
|
|
21
|
+
startupConfig.config = {};
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
return startupConfig;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
getInstance
|
|
29
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable new-cap */
|
|
7
|
+
|
|
8
|
+
const njsAgentConstants = require('./sec-agent-constants');
|
|
9
|
+
const path = require('path').resolve(__dirname, '../../');
|
|
10
|
+
const lodash = require('lodash');
|
|
11
|
+
|
|
12
|
+
let errors = [];
|
|
13
|
+
|
|
14
|
+
const ringBuffer = require('ringbufferjs');
|
|
15
|
+
const bufferedHC = new ringBuffer(5);
|
|
16
|
+
const bufferedErrors = new ringBuffer(5);
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const util = require('util');
|
|
19
|
+
let HC = [];
|
|
20
|
+
let lastHC = {};
|
|
21
|
+
const logs = require('./logging');
|
|
22
|
+
const commonUtils = require('./commonUtils');
|
|
23
|
+
const logger = logs.getLogger();
|
|
24
|
+
const statusFile = njsAgentConstants.STATUS_LOG_FILE;
|
|
25
|
+
|
|
26
|
+
const statusTemplate = 'Snapshot timestamp: : %s\n' +
|
|
27
|
+
`CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
|
|
28
|
+
`SEC_HOME: ${njsAgentConstants.CSEC_HOME}\n` +
|
|
29
|
+
`Agent location: ${path}\n` +
|
|
30
|
+
`Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +
|
|
31
|
+
`Process title: ${process.title}\n` +
|
|
32
|
+
`Process binary: ${process.execPath}\n` +
|
|
33
|
+
'Application location: %s\n' +
|
|
34
|
+
`Current working directory: ${process.cwd()}\n` +
|
|
35
|
+
`Agent mode: ${commonUtils.getCSECmode()}\n` +
|
|
36
|
+
'Application server: %s\n' +
|
|
37
|
+
'Application Framework: %s\n' +
|
|
38
|
+
`Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
|
|
39
|
+
'Instrumentation successful:\n' +
|
|
40
|
+
'Tracking loaded modules in the application:\n' +
|
|
41
|
+
'Policy applied successfully. Policy version is: %s\n' +
|
|
42
|
+
'Started Health Check for Agent\n' +
|
|
43
|
+
'Started Inbound and Outbound monitoring \n' +
|
|
44
|
+
'\nProcess stats:\n%s\n' +
|
|
45
|
+
'\nService stats:\n%s\n' +
|
|
46
|
+
'\nLast 5 errors: \n%s\n' +
|
|
47
|
+
'\nLast 5 Health Checks are:\n %s \n';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Utility to add hc in hc buffer
|
|
51
|
+
* @param {*} healthCheck
|
|
52
|
+
*/
|
|
53
|
+
function addHCtoBuffer (healthCheck) {
|
|
54
|
+
try {
|
|
55
|
+
bufferedHC.enq(healthCheck);
|
|
56
|
+
lastHC = healthCheck;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.debug(error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Utility to add error object in error buffer
|
|
64
|
+
* @param {*} error
|
|
65
|
+
*/
|
|
66
|
+
function addErrortoBuffer (error) {
|
|
67
|
+
try {
|
|
68
|
+
bufferedErrors.enq(error);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.debug(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* utility to format status template with dynamic values
|
|
76
|
+
*/
|
|
77
|
+
function getFormattedData () {
|
|
78
|
+
const Agent = require('./agent').Agent.getAgent();
|
|
79
|
+
const appInfo = Agent.applicationInfo;
|
|
80
|
+
const deployedApplications = appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications;
|
|
81
|
+
const appLoc = deployedApplications[0].deployedPath;
|
|
82
|
+
const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, njsAgentConstants.FRAMEWORK, process.env.WS_HEALTH, appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
|
|
83
|
+
return formattedSnapshot;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* utility to write snapshot in snapshot file
|
|
88
|
+
*/
|
|
89
|
+
function writeSnapshot () {
|
|
90
|
+
const snapshot = getFormattedData();
|
|
91
|
+
commonUtils.removeOlderSnapshots();
|
|
92
|
+
fs.writeFile(statusFile, snapshot, { mode: 0o777 }, function (err) {
|
|
93
|
+
if (err) {
|
|
94
|
+
logger.error(err);
|
|
95
|
+
} else {
|
|
96
|
+
logger.info('Snapshot updated to file: %s', statusFile);
|
|
97
|
+
fs.chmod(statusFile, 0o777, (err) => {
|
|
98
|
+
if (err) {
|
|
99
|
+
addErrortoBuffer(err);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* return buffered error instance.
|
|
107
|
+
*/
|
|
108
|
+
function getRingBufferedErrors () {
|
|
109
|
+
return bufferedErrors;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* return buffered HC list without null, undefined
|
|
114
|
+
*/
|
|
115
|
+
function getBufferedHC () {
|
|
116
|
+
try {
|
|
117
|
+
HC = lodash.compact(bufferedHC._elements);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logger.debug(error);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return HC;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* returns buffered error list without null, undefined
|
|
126
|
+
*/
|
|
127
|
+
function getBufferedErrors () {
|
|
128
|
+
try {
|
|
129
|
+
errors = lodash.compact(bufferedErrors._elements);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.debug(error);
|
|
132
|
+
}
|
|
133
|
+
return errors;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Utility to get key val pairs from json object
|
|
138
|
+
* @param {*} jsonObject
|
|
139
|
+
*/
|
|
140
|
+
function getKeyValPairs (jsonObject) {
|
|
141
|
+
let statStr = njsAgentConstants.EMPTY_STR;
|
|
142
|
+
for (const key in jsonObject) {
|
|
143
|
+
statStr = statStr + key + ': ' + jsonObject[key] + '\n';
|
|
144
|
+
}
|
|
145
|
+
return statStr;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
statusTemplate,
|
|
150
|
+
addHCtoBuffer,
|
|
151
|
+
getFormattedData,
|
|
152
|
+
writeSnapshot,
|
|
153
|
+
addErrortoBuffer,
|
|
154
|
+
getRingBufferedErrors,
|
|
155
|
+
getBufferedHC,
|
|
156
|
+
getBufferedErrors
|
|
157
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates TimeUnit Instance with milliseconds
|
|
10
|
+
* as the base unit.
|
|
11
|
+
*/
|
|
12
|
+
function TimeUnit() {
|
|
13
|
+
this.MILLISECONDS = 1;
|
|
14
|
+
this.NANOSECONDS = 0.000001
|
|
15
|
+
this.MICROSECONDS = 0.001;
|
|
16
|
+
this.SECONDS = 1000;
|
|
17
|
+
this.MINUTES = 60000;
|
|
18
|
+
this.HOURS = 3600000;
|
|
19
|
+
this.DAY = 86400000;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* converts time value from given unit.
|
|
23
|
+
*
|
|
24
|
+
* @param {Number} value
|
|
25
|
+
* @param {String} timeunit
|
|
26
|
+
*
|
|
27
|
+
* @returns milliseconds
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
this.getMillis = function (value, unit, valueOnFail) {
|
|
31
|
+
const UNIT = unit.toString().toUpperCase();
|
|
32
|
+
if (this[UNIT]) {
|
|
33
|
+
try {
|
|
34
|
+
return value * this[UNIT];
|
|
35
|
+
} catch (e) {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return valueOnFail;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = new TimeUnit();
|
|
44
|
+
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const RingBuffer = require('ringbufferjs');
|
|
9
|
+
const LOG_MESSAGES = require('./sec-agent-constants').LOG_MESSAGES;
|
|
10
|
+
|
|
11
|
+
const { Agent } = require('./agent');
|
|
12
|
+
const AgentStatus = require('./agent-status');
|
|
13
|
+
const hc = require('./health-check');
|
|
14
|
+
const applicationInfo = require('./applicationinfo').getInstance();
|
|
15
|
+
const logger = require('./logging').getLogger(applicationInfo.applicationUUID);
|
|
16
|
+
const initLogger = require('./logging').getInitLogger();
|
|
17
|
+
const { WorkerMessage, WORKER_STATUS } = require('./worker-entities');
|
|
18
|
+
const commonUtils = require('./commonUtils');
|
|
19
|
+
const API = require('../../../nr-security-api');
|
|
20
|
+
const NRAgent = API.getNRAgent();
|
|
21
|
+
const {
|
|
22
|
+
SecWebSocket,
|
|
23
|
+
setWebSocketConn,
|
|
24
|
+
aquireWebSocket,
|
|
25
|
+
ResponseHandler: {
|
|
26
|
+
handle: handleServerResponse,
|
|
27
|
+
setLogger: setResponseHandlerLogger
|
|
28
|
+
}
|
|
29
|
+
} = require('./connections/websocket');
|
|
30
|
+
const { HC_INTERVAL_MS } = require('../../resources/config');
|
|
31
|
+
const { IS_LAMBDA_ENV, LAMBDA_ENABLE_RESPONSE_BLOCKING, LOG_DIR } = require('./sec-agent-constants');
|
|
32
|
+
const statusUtils = require('./statusUtils');
|
|
33
|
+
const njsAgentConstants = require('./sec-agent-constants');
|
|
34
|
+
|
|
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
|
+
let wsInstance;
|
|
43
|
+
let dispatcher;
|
|
44
|
+
|
|
45
|
+
const DEFAULT_EVENT_SEND_CB = err => {
|
|
46
|
+
if (err) {
|
|
47
|
+
hc.getInstance().registerEventDrop();
|
|
48
|
+
logger.debug(err);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Initializes the Websocket connection to Validator.
|
|
54
|
+
*/
|
|
55
|
+
const initialize = function () {
|
|
56
|
+
// we would want that the websocket be
|
|
57
|
+
// initialized once only. All ws reconnection
|
|
58
|
+
// and recovery mechanisms must be handled by
|
|
59
|
+
// the SecWebSocket internally.
|
|
60
|
+
if (dispatcher) {
|
|
61
|
+
logger.warn("Websocket must be initialized only once");
|
|
62
|
+
obeyReconnect();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setResponseHandlerLogger(logger);
|
|
67
|
+
|
|
68
|
+
if (workerSupported) {
|
|
69
|
+
initiateInWorkerThread();
|
|
70
|
+
} else {
|
|
71
|
+
initializeInMainThread();
|
|
72
|
+
}
|
|
73
|
+
// we'll dispatch the application info if it is updated
|
|
74
|
+
Agent.eventEmitter.on(Agent.EVENTS.APPLICATION_INFO_UPDATE_EVENT, (applicationInfo) => {
|
|
75
|
+
if (!NRAgent || (NRAgent && applicationInfo.entityGuid)) {
|
|
76
|
+
dispatcher(applicationInfo);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
Agent.eventEmitter.on(Agent.EVENTS.RECONNECT_WS_EVENT, () => {
|
|
80
|
+
setWebSocketConn();
|
|
81
|
+
if (workerSupported) {
|
|
82
|
+
const reConnectMsg = new WorkerMessage(WorkerMessage.TYPE.RECONNECT, {});
|
|
83
|
+
wsWorker.postMessage(reConnectMsg);
|
|
84
|
+
} else {
|
|
85
|
+
wsInstance.reconnect();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// Initiate HC send routine in the provided hcInterval.
|
|
89
|
+
initLogger.info(LOG_MESSAGES.SCHED_SERVICE_START);
|
|
90
|
+
initLogger.info(LOG_MESSAGES.START_HC);
|
|
91
|
+
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
setInterval(sendHC, HC_INTERVAL_MS);
|
|
94
|
+
initLogger.info(LOG_MESSAGES.END_HC);
|
|
95
|
+
sendHC();
|
|
96
|
+
}, 30000);
|
|
97
|
+
|
|
98
|
+
setInterval(commonUtils.logRollOver, 30000);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* This initiates the ws in main thread
|
|
103
|
+
*/
|
|
104
|
+
function initializeInMainThread() {
|
|
105
|
+
logger.info(LOG_MESSAGES.NODE_VERSION_MSG, process.version);
|
|
106
|
+
if (!wsInstance) {
|
|
107
|
+
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
108
|
+
wsInstance = new SecWebSocket(validatorService, {}, logger);
|
|
109
|
+
dispatcher = function dispatcher(message) {
|
|
110
|
+
wsInstance.dispatch(message);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Setup the event listeners
|
|
114
|
+
wsInstance.event.on(SecWebSocket.Events.BUSY, function () {
|
|
115
|
+
busy = true;
|
|
116
|
+
});
|
|
117
|
+
wsInstance.event.on(SecWebSocket.Events.INACTIVE, function () {
|
|
118
|
+
busy = false;
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
return wsInstance;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* This initiates the ws in worker thread
|
|
127
|
+
*/
|
|
128
|
+
function initiateInWorkerThread() {
|
|
129
|
+
if (workerSupported) {
|
|
130
|
+
process.env.WS_WORKER_STATUS = WORKER_STATUS.INITIALIZING;
|
|
131
|
+
try {
|
|
132
|
+
setWebSocketConn();
|
|
133
|
+
const { Worker, SHARE_ENV } = require('worker_threads');
|
|
134
|
+
wsWorker = new Worker(filePath, {
|
|
135
|
+
workerData: {
|
|
136
|
+
applicationInfo
|
|
137
|
+
},
|
|
138
|
+
env: SHARE_ENV
|
|
139
|
+
});
|
|
140
|
+
wsWorker.on('message', handleWorkerMessage);
|
|
141
|
+
wsWorker.on('error', (err) => {
|
|
142
|
+
logger.error('Exception in web socket worker:', err.message);
|
|
143
|
+
wsWorker = new Worker(filePath, {
|
|
144
|
+
workerData: {
|
|
145
|
+
applicationInfo
|
|
146
|
+
}, env: SHARE_ENV
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
dispatcher = function dispatcher(message) {
|
|
150
|
+
if (process.env.WS_WORKER_STATUS !== WORKER_STATUS.RUNNING) {
|
|
151
|
+
localQueue.enq(message);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// websocket is aquired here and then
|
|
155
|
+
// released in worker thread when the
|
|
156
|
+
// handoff to websocket is confirmed
|
|
157
|
+
aquireWebSocket();
|
|
158
|
+
const msg = new WorkerMessage(WorkerMessage.TYPE.MESSAGE, message);
|
|
159
|
+
wsWorker.postMessage(msg);
|
|
160
|
+
};
|
|
161
|
+
logger.info(LOG_MESSAGES.NODE_VERSION_MSG_WORKER, process.version);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.debug('error is:', error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* This handles any messages received from worker
|
|
170
|
+
* if websocket worker is initialized
|
|
171
|
+
*
|
|
172
|
+
* @param {JSON} json
|
|
173
|
+
*/
|
|
174
|
+
function handleWorkerMessage(json) {
|
|
175
|
+
const msg = WorkerMessage.parseFromObject(json);
|
|
176
|
+
switch (msg.type) {
|
|
177
|
+
case WorkerMessage.TYPE.MESSAGE: {
|
|
178
|
+
logger.debug(LOG_MESSAGES.MSG_FROM_WORKKER, msg.getPayload());
|
|
179
|
+
handleServerResponse(msg.getPayload());
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case WorkerMessage.TYPE.FLUSH: {
|
|
183
|
+
const { status } = msg.getPayload();
|
|
184
|
+
if (status === 'flushed') {
|
|
185
|
+
logger.debug('Message from worker thread: Flush Completed');
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case WorkerMessage.TYPE.IC_READY_FOR_EVENTS: {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case WorkerMessage.TYPE.BUSY: {
|
|
193
|
+
busy = true;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case WorkerMessage.TYPE.INACTIVE: {
|
|
197
|
+
busy = false;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case WorkerMessage.TYPE.ERROR: {
|
|
201
|
+
statusUtils.addErrortoBuffer(msg.getPayload().stack);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
default:
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Sends Agent HelthCheck to Validator.
|
|
210
|
+
*/
|
|
211
|
+
const sendHC = async function () {
|
|
212
|
+
if (AgentStatus.getInstance().getStatus() === AgentStatus.CSECAgentStatus.codes.ACTIVE && (NRAgent ? (NRAgent.config.security.enabled && NRAgent.canCollectData()) : true)) {
|
|
213
|
+
let json = hc.getInstance().get();
|
|
214
|
+
json = JSON.parse(json);
|
|
215
|
+
json.policyVersion = applicationInfo.policyVersion;
|
|
216
|
+
json.entityGuid = applicationInfo.entityGuid;
|
|
217
|
+
try {
|
|
218
|
+
json.stats = await commonUtils.getHCStats();
|
|
219
|
+
} catch (error) {
|
|
220
|
+
logger.error(error);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
json.serviceStatus = {};
|
|
224
|
+
json.serviceStatus.websocket = process.env.WS_HEALTH;
|
|
225
|
+
json.serviceStatus.logWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector.log`);
|
|
226
|
+
json.serviceStatus.initLogWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector-init.log`);
|
|
227
|
+
json.serviceStatus.statusLogWriter = await commonUtils.isLogAccessible(njsAgentConstants.STATUS_LOG_FILE);
|
|
228
|
+
json.serviceStatus.agentActiveStat = commonUtils.isAgentActiveState();
|
|
229
|
+
json.serviceStatus.iastRestClient = commonUtils.iastRestClientStatus();
|
|
230
|
+
|
|
231
|
+
hc.getInstance().resetDropCount();
|
|
232
|
+
hc.getInstance().resetProcessedCount();
|
|
233
|
+
hc.getInstance().resetEventSentCount();
|
|
234
|
+
hc.getInstance().resetHttpRequestCount();
|
|
235
|
+
|
|
236
|
+
logger.info('Health Check Status:', JSON.stringify(json));
|
|
237
|
+
statusUtils.addHCtoBuffer(json);
|
|
238
|
+
statusUtils.writeSnapshot();
|
|
239
|
+
getDispatcherAndSendEvent(json);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Will send event asyncronously.
|
|
245
|
+
*
|
|
246
|
+
* @param {JSON} eventJson
|
|
247
|
+
*/
|
|
248
|
+
function getDispatcherAndSendEvent(eventJson) {
|
|
249
|
+
const eventType = eventJson.jsonName;
|
|
250
|
+
if(eventType==='Event'){
|
|
251
|
+
hc.getInstance().registerEventProcessed();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
dispatcher(eventJson);
|
|
255
|
+
|
|
256
|
+
if(eventType==='Event'){
|
|
257
|
+
hc.getInstance().registerEventSent();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Sends passed Event to Validator.
|
|
264
|
+
*
|
|
265
|
+
* @param {Event} event
|
|
266
|
+
* @param {Function} callback (optional)
|
|
267
|
+
*/
|
|
268
|
+
function sendEvent(event, callback = DEFAULT_EVENT_SEND_CB) {
|
|
269
|
+
dispatcher(event);
|
|
270
|
+
callback();
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Flushes local queue to the worker
|
|
275
|
+
*/
|
|
276
|
+
function flushLocalQueue() {
|
|
277
|
+
if (!workerSupported) return;
|
|
278
|
+
|
|
279
|
+
while (!localQueue.isEmpty()) {
|
|
280
|
+
dispatcher(localQueue.deq());
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Waits for websocket to get idel before
|
|
286
|
+
* exiting the lambda function
|
|
287
|
+
* @param {Number} startTime
|
|
288
|
+
*/
|
|
289
|
+
function busyWaitOnWorkerThread(startTime = Date.now()) {
|
|
290
|
+
if (!workerSupported) return;
|
|
291
|
+
|
|
292
|
+
const workerInitiatedOnWait = process.env.WS_WORKER_STATUS === WORKER_STATUS.RUNNING;
|
|
293
|
+
const enableWait = !IS_LAMBDA_ENV || process.env[LAMBDA_ENABLE_RESPONSE_BLOCKING] === 'true';
|
|
294
|
+
|
|
295
|
+
let flushed = false;
|
|
296
|
+
let wait = (
|
|
297
|
+
process.env.CSEC_WS_STATE === SecWebSocket.States.CONNECTING ||
|
|
298
|
+
(
|
|
299
|
+
enableWait &&
|
|
300
|
+
process.env.CSEC_WS_STATE === SecWebSocket.States.BUSY
|
|
301
|
+
)
|
|
302
|
+
) && (startTime + MAX_BUSY_WAIT_TIME > Date.now());
|
|
303
|
+
while (wait) {
|
|
304
|
+
wait = (
|
|
305
|
+
process.env.CSEC_WS_STATE === SecWebSocket.States.CONNECTING ||
|
|
306
|
+
(
|
|
307
|
+
enableWait &&
|
|
308
|
+
process.env.CSEC_WS_STATE === SecWebSocket.States.BUSY
|
|
309
|
+
)
|
|
310
|
+
) && (startTime + MAX_BUSY_WAIT_TIME > Date.now());
|
|
311
|
+
|
|
312
|
+
if (!flushed && !workerInitiatedOnWait && process.env.WS_WORKER_STATUS === WORKER_STATUS.RUNNING) {
|
|
313
|
+
flushed = true;
|
|
314
|
+
flushLocalQueue();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function obeyReconnect(){
|
|
320
|
+
if (workerSupported) {
|
|
321
|
+
const reConnectMsg = new WorkerMessage(WorkerMessage.TYPE.OBEY_RECONNECT, {});
|
|
322
|
+
wsWorker.postMessage(reConnectMsg);
|
|
323
|
+
} else {
|
|
324
|
+
wsInstance.obeyReconnect();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
module.exports = {
|
|
329
|
+
initialize,
|
|
330
|
+
write: sendEvent,
|
|
331
|
+
dispatcher: getDispatcherAndSendEvent,
|
|
332
|
+
flushLocalQueue,
|
|
333
|
+
busyWaitOnWorkerThread,
|
|
334
|
+
obeyReconnect
|
|
335
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const MESSAGE = 'MESSAGE';
|
|
7
|
+
const FLUSH = 'FLUSH';
|
|
8
|
+
const BUSY = 'BUSY';
|
|
9
|
+
const INACTIVE = 'INACTIVE';
|
|
10
|
+
const RECONNECT = 'RECONNECT';
|
|
11
|
+
const UPDATE_APPLICATION_INFO = 'UPDATE APPLICATION INFO';
|
|
12
|
+
const IC_READY_FOR_EVENTS = 'IC READY FOR EVENTS';
|
|
13
|
+
const INITIALIZING_STATUS = 'initializing';
|
|
14
|
+
const NONE_STATUS = '';
|
|
15
|
+
const RUNNING_STATUS = 'running';
|
|
16
|
+
const ENDED_STATUS = 'ended';
|
|
17
|
+
const ERROR = 'ERROR';
|
|
18
|
+
const OBEY_RECONNECT = 'obey-reconnect';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This defines and constructs a worker message.
|
|
22
|
+
* @param {String} type
|
|
23
|
+
* @param {Object} payload
|
|
24
|
+
*/
|
|
25
|
+
function WorkerMessage (type, payload) {
|
|
26
|
+
this.type = type;
|
|
27
|
+
this.payload = payload;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
WorkerMessage.prototype = {};
|
|
31
|
+
WorkerMessage.prototype.constructor = WorkerMessage;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns the type of worker message
|
|
35
|
+
*/
|
|
36
|
+
WorkerMessage.prototype.getType = function getType () {
|
|
37
|
+
return this.type;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* returns the actual payload of message
|
|
42
|
+
*/
|
|
43
|
+
WorkerMessage.prototype.getPayload = function getPayload () {
|
|
44
|
+
return this.payload;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
WorkerMessage.TYPE = {
|
|
48
|
+
MESSAGE,
|
|
49
|
+
FLUSH,
|
|
50
|
+
BUSY,
|
|
51
|
+
INACTIVE,
|
|
52
|
+
RECONNECT,
|
|
53
|
+
UPDATE_APPLICATION_INFO,
|
|
54
|
+
IC_READY_FOR_EVENTS,
|
|
55
|
+
ERROR,
|
|
56
|
+
OBEY_RECONNECT
|
|
57
|
+
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* parses a Worker thread from simple object
|
|
62
|
+
* @param {Object} obj
|
|
63
|
+
*/
|
|
64
|
+
WorkerMessage.parseFromObject = function parseFromObject (obj) {
|
|
65
|
+
if (obj && obj.type && obj.payload) {
|
|
66
|
+
return new WorkerMessage(obj.type, obj.payload);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const WORKER_STATUS = {
|
|
73
|
+
NONE: NONE_STATUS,
|
|
74
|
+
INITIALIZING: INITIALIZING_STATUS,
|
|
75
|
+
RUNNING: RUNNING_STATUS,
|
|
76
|
+
ENDED: ENDED_STATUS
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
WorkerMessage,
|
|
81
|
+
WORKER_STATUS
|
|
82
|
+
};
|