@newrelic/security-agent 1.5.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/THIRD_PARTY_NOTICES.md +48 -1
- package/lib/instrumentation-security/core/constants.js +8 -1
- package/lib/instrumentation-security/core/event-constants.js +5 -3
- package/lib/instrumentation-security/core/request-manager.js +40 -8
- package/lib/instrumentation-security/core/sec-utils.js +6 -5
- package/lib/instrumentation-security/hooks/director/nr-director.js +6 -6
- package/lib/instrumentation-security/hooks/express/nr-express.js +11 -8
- package/lib/instrumentation-security/hooks/fastify/nr-fastify.js +6 -6
- package/lib/instrumentation-security/hooks/fs/nr-fs.js +11 -28
- package/lib/instrumentation-security/hooks/grpc-js/nr-grpc.js +4 -4
- package/lib/instrumentation-security/hooks/hapi/nr-hapi.js +7 -7
- package/lib/instrumentation-security/hooks/http/nr-http.js +137 -13
- package/lib/instrumentation-security/hooks/koa/@koa/nr-@koa-router.js +5 -5
- package/lib/instrumentation-security/hooks/ldap/nr-ldap.js +4 -0
- package/lib/instrumentation-security/hooks/mongodb/nr-mongodb.js +4 -0
- package/lib/instrumentation-security/hooks/mysql/nr-mysql.js +4 -0
- package/lib/instrumentation-security/hooks/native/nr-childProcess.js +4 -0
- package/lib/instrumentation-security/hooks/postgres/nr-postgres.js +4 -0
- package/lib/instrumentation-security/hooks/restify/nr-restify.js +7 -7
- package/lib/instrumentation-security/hooks/undici/nr-undici.js +4 -0
- package/lib/instrumentation-security/hooks/xpath/nr-xpath.js +4 -0
- package/lib/nr-security-agent/index.js +38 -5
- package/lib/nr-security-agent/lib/core/Auth-headers.js +20 -0
- package/lib/nr-security-agent/lib/core/ExceptionReporting.js +1 -0
- package/lib/nr-security-agent/lib/core/Policy.js +0 -12
- package/lib/nr-security-agent/lib/core/agent.js +5 -1
- package/lib/nr-security-agent/lib/core/applicationinfo.js +2 -2
- package/lib/nr-security-agent/lib/core/commonUtils.js +141 -45
- package/lib/nr-security-agent/lib/core/connections/websocket/proxy-util.js +61 -0
- package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +34 -6
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +36 -6
- package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js +5 -1
- package/lib/nr-security-agent/lib/core/connections/websocket/response/ws-reconnect.js +7 -4
- package/lib/nr-security-agent/lib/core/connections/websocket/websocket.js +32 -14
- package/lib/nr-security-agent/lib/core/event.js +2 -2
- package/lib/nr-security-agent/lib/core/health-check.js +8 -2
- package/lib/nr-security-agent/lib/core/sec-agent-constants.js +2 -3
- package/lib/nr-security-agent/lib/core/sec-util.js +40 -4
- package/lib/nr-security-agent/lib/core/websocket-client.js +35 -12
- package/lib/nr-security-api/index.js +47 -15
- package/package.json +10 -4
- package/lib/nr-security-agent/lib/core/statusUtils.js +0 -165
|
@@ -34,8 +34,8 @@ function BasicInfo() {
|
|
|
34
34
|
this.policyVersion = (!lodash.isEmpty(policyManager.getInstance().data)) ? policyManager.getInstance().data.version : 'DEFAULT';
|
|
35
35
|
|
|
36
36
|
if (NRAgent && NRAgent.config) {
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
37
|
+
this.appAccountId = NRAgent.config.account_id;
|
|
38
|
+
this.appEntityGuid = NRAgent.config.entity_guid;
|
|
39
39
|
this.linkingMetadata = LinkingMetaData.getLinkingMetadata();
|
|
40
40
|
this.linkingMetadata.agentRunId = NRAgent.config.run_id;
|
|
41
41
|
}
|
|
@@ -14,7 +14,8 @@ const NODE_JS_LITERAL = 'Node.js:';
|
|
|
14
14
|
|
|
15
15
|
let NODE_SERVER;
|
|
16
16
|
let instance;
|
|
17
|
-
|
|
17
|
+
const logs = require('./logging');
|
|
18
|
+
const logger = logs.getLogger();
|
|
18
19
|
const API = require('../../../nr-security-api');
|
|
19
20
|
const NRAgent = API.getNRAgent();
|
|
20
21
|
|
|
@@ -35,6 +36,7 @@ module.exports.getInstance = function () {
|
|
|
35
36
|
return instance;
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
|
|
38
40
|
/**
|
|
39
41
|
* Creates a new HealthCheck object.
|
|
40
42
|
*
|
|
@@ -46,7 +48,8 @@ function HC(info) {
|
|
|
46
48
|
this.applicationUUID = info.applicationUUID;
|
|
47
49
|
this.eventType = 'sec_health_check_lc';
|
|
48
50
|
if (NRAgent && NRAgent.config) {
|
|
49
|
-
this.
|
|
51
|
+
this.appEntityGuid = NRAgent.config.entity_guid;
|
|
52
|
+
this.appAccountId = NRAgent.config.account_id;
|
|
50
53
|
}
|
|
51
54
|
this.protectedServer = NODE_SERVER;
|
|
52
55
|
this.eventDropCount = 0;
|
|
@@ -62,6 +65,9 @@ function HC(info) {
|
|
|
62
65
|
this.iastEventStats = EventStats.getInstance();
|
|
63
66
|
this.raspEventStats = EventStats.getInstance();
|
|
64
67
|
this.exitEventStats = EventStats.getInstance();
|
|
68
|
+
this.scanStartTime = require('./commonUtils').scanStartTime;
|
|
69
|
+
this.trafficStartedTime = require('./commonUtils').trafficStartedTime;
|
|
70
|
+
this.procStartTime = Date.now() - Math.floor(process.uptime() * 1000);
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
/**
|
|
@@ -24,15 +24,14 @@ module.exports = {
|
|
|
24
24
|
CSEC_HOME_TMP: `${CSEC_HOME}${sep}nr-security-home${sep}tmp${sep}language-agent${sep}${process.env.applicationUUID}`,
|
|
25
25
|
AGENT_START_TIME: agentStartTime,
|
|
26
26
|
FRAMEWORK: '',
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
|
|
29
28
|
DB_LIST: {
|
|
30
29
|
MYSQL: 'MYSQL',
|
|
31
30
|
MYSQL2: 'MYSQL',
|
|
32
31
|
POSTGRES: 'POSTGRES',
|
|
33
32
|
MONGODB: 'MONGODB',
|
|
34
33
|
ORACLEDB: 'ORACLEDB',
|
|
35
|
-
MSSQL: 'MSSQL',
|
|
34
|
+
MSSQL: 'MSSQL',
|
|
36
35
|
SQLITE3: 'SQLITE3'
|
|
37
36
|
},
|
|
38
37
|
SERVER_COMMAND: {
|
|
@@ -9,16 +9,17 @@ const { SecEvent } = require('./event');
|
|
|
9
9
|
const logs = require('./logging');
|
|
10
10
|
const { Agent } = require('./agent');
|
|
11
11
|
const shaUtil = require('./sha-size-util');
|
|
12
|
-
const {
|
|
12
|
+
const { NR_CSEC_FUZZ_REQUEST_ID, COLON, VULNERABLE, EXITEVENT, EMPTY_STR, SEVERE, HYPHEN } = require('./sec-agent-constants');
|
|
13
13
|
const API = require('../../../nr-security-api');
|
|
14
14
|
const NRAgent = API.getNRAgent();
|
|
15
15
|
const HC = require('./health-check');
|
|
16
16
|
const LogMessage = require('./LogMessage');
|
|
17
17
|
const commonUtils = require('./commonUtils');
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const appInfo = require('./applicationinfo').getInstance();
|
|
20
20
|
|
|
21
21
|
let firstEventSent = false;
|
|
22
|
+
let appDir = EMPTY_STR;
|
|
22
23
|
|
|
23
24
|
const events = require('events');
|
|
24
25
|
const eventEmitter = undefined;
|
|
@@ -27,6 +28,20 @@ let logger = logs.getLogger();
|
|
|
27
28
|
const initLogger = logs.getInitLogger();
|
|
28
29
|
|
|
29
30
|
const NODE_SER = 'node-serialize/lib/serialize.js';
|
|
31
|
+
const SECURE_COOKIE = 'SECURE_COOKIE';
|
|
32
|
+
|
|
33
|
+
const insecureSettingApiIdSet = new Set();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Utility to remove insecureSettingAPIIds after 30 mins from the set.
|
|
37
|
+
* @param {*} entry
|
|
38
|
+
*/
|
|
39
|
+
function addInsecureSettingApiId(entry) {
|
|
40
|
+
insecureSettingApiIdSet.add(entry);
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
insecureSettingApiIdSet.delete(entry);
|
|
43
|
+
}, 30 * 60 * 1000);
|
|
44
|
+
}
|
|
30
45
|
|
|
31
46
|
function getSecEventEmitter() {
|
|
32
47
|
if (eventEmitter) {
|
|
@@ -68,6 +83,7 @@ const sleep = (timeout) => new Promise((resolve) => {
|
|
|
68
83
|
function generateSecEvent(securityMetadata) {
|
|
69
84
|
const traceObject = securityMetadata.traceObject;
|
|
70
85
|
const request = securityMetadata.request;
|
|
86
|
+
let appServerInfo = {};
|
|
71
87
|
|
|
72
88
|
if (request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
|
|
73
89
|
HC.getInstance().iastEventStats.processed++;
|
|
@@ -75,6 +91,16 @@ function generateSecEvent(securityMetadata) {
|
|
|
75
91
|
else {
|
|
76
92
|
HC.getInstance().raspEventStats.processed++;
|
|
77
93
|
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
appDir = appInfo.serverInfo.deployedApplications[0].deployedPath;
|
|
97
|
+
appServerInfo.applicationDirectory = appDir;
|
|
98
|
+
appServerInfo.serverBaseDirectory = appDir;
|
|
99
|
+
appServerInfo.connectionConfiguration = { [request.serverPort]: request.protocol };
|
|
100
|
+
|
|
101
|
+
} catch (error) {
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
try {
|
|
79
105
|
let stakTrace = traceObject.stacktrace;
|
|
80
106
|
let uri = EMPTY_STR;
|
|
@@ -84,8 +110,15 @@ function generateSecEvent(securityMetadata) {
|
|
|
84
110
|
|
|
85
111
|
let apiId = shaUtil.getSHA256ForData(traceObject.stacktrace.join('|') + uri);
|
|
86
112
|
apiId = securityMetadata.eventType + HYPHEN + apiId;
|
|
113
|
+
if (securityMetadata.eventType == SECURE_COOKIE) {
|
|
114
|
+
if (insecureSettingApiIdSet.has(apiId)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
addInsecureSettingApiId(apiId);
|
|
118
|
+
}
|
|
87
119
|
const agentModule = Agent.getAgent();
|
|
88
120
|
const metaData = {};
|
|
121
|
+
metaData.appServerInfo = appServerInfo;
|
|
89
122
|
if (traceObject.sourceDetails && traceObject.sourceDetails.evalObj) {
|
|
90
123
|
metaData.triggerViaRCI = true;
|
|
91
124
|
metaData.rciMethodsCalls = traceObject.sourceDetails.evalObj.invokedCalls;
|
|
@@ -101,10 +134,10 @@ function generateSecEvent(securityMetadata) {
|
|
|
101
134
|
const isAPIBlocked = false;
|
|
102
135
|
const event = new SecEvent(traceObject.sourceDetails.fileName, traceObject.sourceDetails.funcName, traceObject.sourceDetails.lineNumber, securityMetadata.interceptedArgs, securityMetadata.executionId, agentModule.applicationInfo, securityMetadata.request, securityMetadata.eventType, traceObject.sourceDetails.source, securityMetadata.eventCategory, metaData, stakTrace, apiId, isAPIBlocked);
|
|
103
136
|
event.metaData.isClientDetectedFromXFF = isClientDetectedFromXFF;
|
|
137
|
+
event.metaData.skipScanParameters = NRAgent.config.security.exclude_from_iast_scan.http_request_parameters;
|
|
104
138
|
event.httpRequest.route = uri;
|
|
105
139
|
return event;
|
|
106
140
|
} catch (e) {
|
|
107
|
-
statusUtils.addErrortoBuffer(e);
|
|
108
141
|
logger.error("Error in generating event:", e);
|
|
109
142
|
|
|
110
143
|
const logMessage = new LogMessage.logMessage(SEVERE, 'Error while generating event', __filename, e);
|
|
@@ -159,9 +192,12 @@ function generateExitEvent(secEvent) {
|
|
|
159
192
|
|
|
160
193
|
function sendEvent(secEvent) {
|
|
161
194
|
const agentModule = Agent.getAgent();
|
|
162
|
-
if (agentModule.status.getStatus() != 'connecting' && NRAgent.config.security.enabled) {
|
|
195
|
+
if (agentModule.status.getStatus() != 'connecting' && NRAgent.config.security.enabled && agentModule.status.getStatus() == 'active') {
|
|
163
196
|
agentModule.client.dispatcher(secEvent);
|
|
164
197
|
if (!firstEventSent && agentModule.status.getStatus() === 'active') {
|
|
198
|
+
if(commonUtils.trafficStartedTime == 0){
|
|
199
|
+
commonUtils.trafficStartedTime = Date.now();
|
|
200
|
+
}
|
|
165
201
|
initLogger.info("[STEP-8] => First event sent for validation. Security Agent started successfully", JSON.stringify(secEvent));
|
|
166
202
|
firstEventSent = true;
|
|
167
203
|
}
|
|
@@ -25,7 +25,6 @@ const {
|
|
|
25
25
|
} = require('./connections/websocket');
|
|
26
26
|
const { HC_INTERVAL_MS } = require('../../resources/config');
|
|
27
27
|
const { LOG_DIR, EXITEVENT, JSON_NAME } = require('./sec-agent-constants');
|
|
28
|
-
const statusUtils = require('./statusUtils');
|
|
29
28
|
const njsAgentConstants = require('./sec-agent-constants');
|
|
30
29
|
|
|
31
30
|
let wsInstance;
|
|
@@ -71,22 +70,22 @@ const initialize = function () {
|
|
|
71
70
|
setInterval(sendHC, HC_INTERVAL_MS);
|
|
72
71
|
sendHC();
|
|
73
72
|
try {
|
|
74
|
-
let apiEndpoints =
|
|
75
|
-
let data
|
|
76
|
-
logger.debug("All API end points of the application is:",JSON.stringify(data));
|
|
73
|
+
let apiEndpoints = require('../../../instrumentation-security/core/route-manager').getAllAPIEndPoints();
|
|
74
|
+
let data = require('./apiEndpoints').APIEndPoint(apiEndpoints);
|
|
75
|
+
logger.debug("All API end points of the application is:", JSON.stringify(data));
|
|
77
76
|
getDispatcherAndSendEvent(data);
|
|
78
77
|
require('../core/ExceptionReporting').startExceptionSendingSchedular();
|
|
79
78
|
} catch (error) {
|
|
80
|
-
logger.debug("Error while processing API endpoints",error);
|
|
79
|
+
logger.debug("Error while processing API endpoints", error);
|
|
81
80
|
}
|
|
82
|
-
|
|
81
|
+
|
|
83
82
|
try {
|
|
84
83
|
const framework = NRAgent.environment.get('Framework')[0];
|
|
85
84
|
const dispatcher = NRAgent.environment.get('Dispatcher')[0];
|
|
86
85
|
const logMessage = new LogMessage.logMessage("INFO", `Detected framework: ${framework} and dispatcher: ${dispatcher}`, __filename, null);
|
|
87
86
|
commonUtils.addLogEventtoBuffer(logMessage);
|
|
88
87
|
} catch (error) {
|
|
89
|
-
logger.debug("Error while extracting data from APM environment",error);
|
|
88
|
+
logger.debug("Error while extracting data from APM environment", error);
|
|
90
89
|
}
|
|
91
90
|
}, 20000);
|
|
92
91
|
|
|
@@ -104,13 +103,26 @@ function initializeInMainThread() {
|
|
|
104
103
|
const validatorService = commonUtils.getValidatorServiceEndpointURL();
|
|
105
104
|
wsInstance = new SecWebSocket(validatorService, {}, logger);
|
|
106
105
|
dispatcher = function dispatcher(message) {
|
|
107
|
-
|
|
106
|
+
if (Agent.getAgent().status.getStatus() == 'active') {
|
|
107
|
+
wsInstance.dispatch(message);
|
|
108
|
+
}
|
|
108
109
|
};
|
|
109
110
|
} else {
|
|
110
111
|
return wsInstance;
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
function getTestIdentifier() {
|
|
116
|
+
try {
|
|
117
|
+
let authHeaders = require('./Auth-headers').getInstance();
|
|
118
|
+
let testIdentifer = authHeaders['NR-CSEC-IAST-TEST-IDENTIFIER'] ? authHeaders['NR-CSEC-IAST-TEST-IDENTIFIER'] : '';
|
|
119
|
+
return testIdentifer
|
|
120
|
+
} catch (error) {
|
|
121
|
+
logger.debug("unable to get test identifier", error);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
114
126
|
/**
|
|
115
127
|
* Sends Agent HelthCheck to Validator.
|
|
116
128
|
*/
|
|
@@ -120,6 +132,10 @@ const sendHC = async function () {
|
|
|
120
132
|
json = JSON.parse(json);
|
|
121
133
|
json.policyVersion = applicationInfo.policyVersion;
|
|
122
134
|
json.entityGuid = applicationInfo.entityGuid;
|
|
135
|
+
if (NRAgent && NRAgent.config) {
|
|
136
|
+
json.appEntityGuid = NRAgent.config.entity_guid;
|
|
137
|
+
json.appAccountId = NRAgent.config.account_id;
|
|
138
|
+
}
|
|
123
139
|
|
|
124
140
|
try {
|
|
125
141
|
json.stats = await commonUtils.getHCStats();
|
|
@@ -131,7 +147,6 @@ const sendHC = async function () {
|
|
|
131
147
|
json.serviceStatus.websocket = commonUtils.getWSHealthStatus();
|
|
132
148
|
json.serviceStatus.logWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector.log`);
|
|
133
149
|
json.serviceStatus.initLogWriter = await commonUtils.isLogAccessible(`${LOG_DIR}node-security-collector-init.log`);
|
|
134
|
-
json.serviceStatus.statusLogWriter = await commonUtils.isLogAccessible(njsAgentConstants.STATUS_LOG_FILE);
|
|
135
150
|
json.serviceStatus.agentActiveStat = commonUtils.isAgentActiveState();
|
|
136
151
|
json.serviceStatus.iastRestClient = commonUtils.iastRestClientStatus();
|
|
137
152
|
|
|
@@ -141,15 +156,18 @@ const sendHC = async function () {
|
|
|
141
156
|
json.eventSendRejectionCount = json.eventRejectionCount;
|
|
142
157
|
json.eventSendErrorCount = json.eventProcessingErrorCount;
|
|
143
158
|
|
|
159
|
+
json.scanStartTime = commonUtils.scanStartTime;
|
|
160
|
+
json.trafficStartedTime = commonUtils.trafficStartedTime;
|
|
161
|
+
json.iastTestIdentifer = getTestIdentifier();
|
|
162
|
+
|
|
144
163
|
hc.getInstance().resetDropCount();
|
|
145
164
|
hc.getInstance().resetProcessedCount();
|
|
146
165
|
hc.getInstance().resetEventSentCount();
|
|
147
166
|
hc.getInstance().resetHttpRequestCount();
|
|
148
167
|
hc.getInstance().resetEventStats();
|
|
149
168
|
|
|
169
|
+
|
|
150
170
|
logger.info('Health Check Status:', JSON.stringify(json));
|
|
151
|
-
statusUtils.addHCtoBuffer(json);
|
|
152
|
-
statusUtils.writeSnapshot();
|
|
153
171
|
getDispatcherAndSendEvent(json);
|
|
154
172
|
}
|
|
155
173
|
};
|
|
@@ -190,9 +208,14 @@ function obeyReconnect() {
|
|
|
190
208
|
wsInstance.obeyReconnect();
|
|
191
209
|
}
|
|
192
210
|
|
|
211
|
+
function closeWS() {
|
|
212
|
+
wsInstance.closeWS();
|
|
213
|
+
}
|
|
214
|
+
|
|
193
215
|
module.exports = {
|
|
194
216
|
initialize,
|
|
195
217
|
write: sendEvent,
|
|
196
218
|
dispatcher: getDispatcherAndSendEvent,
|
|
197
|
-
obeyReconnect
|
|
219
|
+
obeyReconnect,
|
|
220
|
+
closeWS
|
|
198
221
|
};
|
|
@@ -10,7 +10,39 @@ let NRAgent;
|
|
|
10
10
|
*
|
|
11
11
|
* @returns NewRelic Agent instance
|
|
12
12
|
*/
|
|
13
|
-
function getNRAgent(){
|
|
13
|
+
function getNRAgent() {
|
|
14
|
+
if (NRAgent && NRAgent.config && !NRAgent.config.security.scan_schedule) {
|
|
15
|
+
NRAgent.config.security.scan_schedule = {};
|
|
16
|
+
NRAgent.config.security.scan_schedule.delay = 0;
|
|
17
|
+
NRAgent.config.security.scan_schedule.duration = 0;
|
|
18
|
+
NRAgent.config.security.scan_schedule.schedule = '';
|
|
19
|
+
NRAgent.config.security.scan_schedule.always_sample_traces = false;
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
if ( NRAgent && NRAgent.config && !NRAgent.config.security.scan_controllers) {
|
|
23
|
+
NRAgent.config.security.scan_controllers = {};
|
|
24
|
+
NRAgent.config.security.scan_controllers.iast_scan_request_rate_limit = 3600;
|
|
25
|
+
NRAgent.config.security.scan_controllers.scan_instance_count = 0;
|
|
26
|
+
}
|
|
27
|
+
if (NRAgent && NRAgent.config && !NRAgent.config.security.exclude_from_iast_scan) {
|
|
28
|
+
NRAgent.config.security.exclude_from_iast_scan = {};
|
|
29
|
+
NRAgent.config.security.exclude_from_iast_scan.api = [];
|
|
30
|
+
NRAgent.config.security.exclude_from_iast_scan.http_request_parameters = {};
|
|
31
|
+
NRAgent.config.security.exclude_from_iast_scan.http_request_parameters.header = [];
|
|
32
|
+
NRAgent.config.security.exclude_from_iast_scan.http_request_parameters.query = [];
|
|
33
|
+
NRAgent.config.security.exclude_from_iast_scan.http_request_parameters.body = [];
|
|
34
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category = {};
|
|
35
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.insecure_settings = false;
|
|
36
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.invalid_file_access = false;
|
|
37
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.sql_injection = false;
|
|
38
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.nosql_injection = false;
|
|
39
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.ldap_injection = false;
|
|
40
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.javascript_injection = false;
|
|
41
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.command_injection = false;
|
|
42
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.xpath_injection = false;
|
|
43
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.ssrf = false;
|
|
44
|
+
NRAgent.config.security.exclude_from_iast_scan.iast_detection_category.rxss = false;
|
|
45
|
+
}
|
|
14
46
|
return NRAgent;
|
|
15
47
|
}
|
|
16
48
|
|
|
@@ -18,7 +50,7 @@ function getNRAgent(){
|
|
|
18
50
|
* API to set newrelic agent object.
|
|
19
51
|
* @param {*} agent
|
|
20
52
|
*/
|
|
21
|
-
function setNRAgent(agent){
|
|
53
|
+
function setNRAgent(agent) {
|
|
22
54
|
NRAgent = agent;
|
|
23
55
|
}
|
|
24
56
|
|
|
@@ -26,11 +58,11 @@ function setNRAgent(agent){
|
|
|
26
58
|
* API to get security agent
|
|
27
59
|
* @returns Security Agent instanace
|
|
28
60
|
*/
|
|
29
|
-
function getSecAgent(){
|
|
61
|
+
function getSecAgent() {
|
|
30
62
|
const Agent = require('../nr-security-agent/lib/core/agent').Agent;
|
|
31
63
|
const csecAgent = Agent.getAgent();
|
|
32
|
-
if(csecAgent){
|
|
33
|
-
|
|
64
|
+
if (csecAgent) {
|
|
65
|
+
return csecAgent;
|
|
34
66
|
}
|
|
35
67
|
}
|
|
36
68
|
|
|
@@ -38,11 +70,11 @@ function getSecAgent(){
|
|
|
38
70
|
* API to get status of security agent
|
|
39
71
|
* @returns Security Agent status
|
|
40
72
|
*/
|
|
41
|
-
function getSecAgentStatus(){
|
|
73
|
+
function getSecAgentStatus() {
|
|
42
74
|
const Agent = require('../nr-security-agent/lib/core/agent').Agent;
|
|
43
|
-
if(Agent.getAgent()){
|
|
75
|
+
if (Agent.getAgent()) {
|
|
44
76
|
return Agent.getAgent().status.getStatus()
|
|
45
|
-
|
|
77
|
+
}
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
/**
|
|
@@ -50,15 +82,15 @@ function getSecAgentStatus(){
|
|
|
50
82
|
* @param {*} securityMetadata
|
|
51
83
|
* @returns security event object
|
|
52
84
|
*/
|
|
53
|
-
function generateSecEvent(securityMetadata){
|
|
54
|
-
|
|
85
|
+
function generateSecEvent(securityMetadata) {
|
|
86
|
+
return require('../nr-security-agent/lib/core/sec-util').generateSecEvent(securityMetadata);
|
|
55
87
|
}
|
|
56
88
|
|
|
57
89
|
/**
|
|
58
90
|
* API to send security event
|
|
59
91
|
* @param {*} secEvent
|
|
60
92
|
*/
|
|
61
|
-
function sendEvent(secEvent){
|
|
93
|
+
function sendEvent(secEvent) {
|
|
62
94
|
require('../nr-security-agent/lib/core/sec-util').sendEvent(secEvent);
|
|
63
95
|
}
|
|
64
96
|
|
|
@@ -66,7 +98,7 @@ function sendEvent(secEvent){
|
|
|
66
98
|
* API to get logger instance
|
|
67
99
|
* @returns
|
|
68
100
|
*/
|
|
69
|
-
function getLogger(){
|
|
101
|
+
function getLogger() {
|
|
70
102
|
return require('../nr-security-agent/lib/core/logging').getLogger();
|
|
71
103
|
}
|
|
72
104
|
/**
|
|
@@ -76,7 +108,7 @@ function getLogger(){
|
|
|
76
108
|
* @param {*} responseHeaders
|
|
77
109
|
* @returns
|
|
78
110
|
*/
|
|
79
|
-
function checkForReflectedXSS(secRequest, responseBody, responseHeaders){
|
|
111
|
+
function checkForReflectedXSS(secRequest, responseBody, responseHeaders) {
|
|
80
112
|
return require('../nr-security-agent/lib/core/xss-utils').checkForReflectedXSS(secRequest, responseBody, responseHeaders);
|
|
81
113
|
}
|
|
82
114
|
|
|
@@ -84,7 +116,7 @@ function checkForReflectedXSS(secRequest, responseBody, responseHeaders){
|
|
|
84
116
|
* API to get security agent policy
|
|
85
117
|
* @returns security agent policy
|
|
86
118
|
*/
|
|
87
|
-
function getPolicy(){
|
|
119
|
+
function getPolicy() {
|
|
88
120
|
return require('../nr-security-agent/lib/core/Policy').getInstance();
|
|
89
121
|
}
|
|
90
122
|
|
|
@@ -92,7 +124,7 @@ function getPolicy(){
|
|
|
92
124
|
* API to generate Exit event
|
|
93
125
|
* @param {*} event
|
|
94
126
|
*/
|
|
95
|
-
function generateExitEvent(event){
|
|
127
|
+
function generateExitEvent(event) {
|
|
96
128
|
require('../nr-security-agent/lib/core/sec-util').generateExitEvent(event);
|
|
97
129
|
}
|
|
98
130
|
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/security-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "New Relic Security Agent for Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"jsonVersion": "1.2.
|
|
6
|
+
"jsonVersion": "1.2.9",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|
|
9
9
|
"name": "Sumit Suthar",
|
|
10
10
|
"email": "ssuthar@newrelic.com",
|
|
11
|
-
"web": "
|
|
11
|
+
"web": "https://newrelic.com"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "Pratik Gunjetiya",
|
|
15
15
|
"email": "pgunjetiya@newrelic.com",
|
|
16
|
-
"web": "
|
|
16
|
+
"web": "https://newrelic.com"
|
|
17
17
|
}
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"New Relic Security Agent",
|
|
29
29
|
"Node.js Application Security"
|
|
30
30
|
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18",
|
|
33
|
+
"npm": ">=6.0.0"
|
|
34
|
+
},
|
|
31
35
|
"author": "newrelic",
|
|
32
36
|
"license": "New Relic Software License v1.0",
|
|
33
37
|
"repository": {
|
|
@@ -38,10 +42,12 @@
|
|
|
38
42
|
"axios": "^1.7.4",
|
|
39
43
|
"check-disk-space": "^3.4.0",
|
|
40
44
|
"content-type": "^1.0.5",
|
|
45
|
+
"cron": "^3.1.7",
|
|
41
46
|
"fast-safe-stringify": "^2.1.1",
|
|
42
47
|
"find-package-json": "^1.2.0",
|
|
43
48
|
"hash.js": "^1.1.7",
|
|
44
49
|
"html-entities": "^2.3.6",
|
|
50
|
+
"https-proxy-agent": "^7.0.4",
|
|
45
51
|
"is-invalid-path": "^1.0.2",
|
|
46
52
|
"js-yaml": "^4.1.0",
|
|
47
53
|
"jsonschema": "^1.4.1",
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
3
|
-
* SPDX-License-Identifier: New Relic Software License v1.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/* eslint-disable new-cap */
|
|
7
|
-
|
|
8
|
-
const njsAgentConstants = require('./sec-agent-constants');
|
|
9
|
-
const agentConfig = require('../../resources/config');
|
|
10
|
-
const { AGENT_DIR } = agentConfig;
|
|
11
|
-
const lodash = require('lodash');
|
|
12
|
-
|
|
13
|
-
let errors = [];
|
|
14
|
-
|
|
15
|
-
const ringBuffer = require('ringbufferjs');
|
|
16
|
-
const bufferedHC = new ringBuffer(5);
|
|
17
|
-
const bufferedErrors = new ringBuffer(5);
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const util = require('util');
|
|
20
|
-
let HC = [];
|
|
21
|
-
let lastHC = {};
|
|
22
|
-
const logs = require('./logging');
|
|
23
|
-
const commonUtils = require('./commonUtils');
|
|
24
|
-
const logger = logs.getLogger();
|
|
25
|
-
const statusFile = njsAgentConstants.STATUS_LOG_FILE;
|
|
26
|
-
const sep = require('path').sep;
|
|
27
|
-
|
|
28
|
-
const statusTemplate = 'Snapshot timestamp: : %s\n' +
|
|
29
|
-
`CSEC Agent start timestamp: ${njsAgentConstants.AGENT_START_TIME} with application uuid:${commonUtils.getUUID()}\n` +
|
|
30
|
-
`CSEC_HOME: ${njsAgentConstants.CSEC_HOME}${sep}nr-security-home${sep}\n` +
|
|
31
|
-
`Agent location: ${AGENT_DIR}\n` +
|
|
32
|
-
`Using CSEC Agent for Node.js, Node version:${process.version}, PID:${process.pid}\n` +
|
|
33
|
-
`Process title: ${process.title}\n` +
|
|
34
|
-
`Process binary: ${process.execPath}\n` +
|
|
35
|
-
'Application location: %s\n' +
|
|
36
|
-
`Current working directory: ${process.cwd()}\n` +
|
|
37
|
-
`Agent mode: ${commonUtils.getCSECmode()}\n` +
|
|
38
|
-
'Application server: %s\n' +
|
|
39
|
-
`Application Framework: %s\n` +
|
|
40
|
-
`Websocket connection to Prevent Web: ${commonUtils.getValidatorServiceEndpointURL()}, Status: %s\n` +
|
|
41
|
-
'Instrumentation successful\n' +
|
|
42
|
-
'Tracking loaded modules in the application\n' +
|
|
43
|
-
'Policy applied successfully. Policy version is: %s\n' +
|
|
44
|
-
'Started Health Check for Agent\n' +
|
|
45
|
-
'Started Inbound and Outbound monitoring \n' +
|
|
46
|
-
'\nProcess stats:\n%s\n' +
|
|
47
|
-
'\nService stats:\n%s\n' +
|
|
48
|
-
'\nLast 5 errors: \n%s\n' +
|
|
49
|
-
'\nLast 5 Health Checks are:\n %s \n';
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Utility to add hc in hc buffer
|
|
53
|
-
* @param {*} healthCheck
|
|
54
|
-
*/
|
|
55
|
-
function addHCtoBuffer(healthCheck) {
|
|
56
|
-
try {
|
|
57
|
-
bufferedHC.enq(healthCheck);
|
|
58
|
-
lastHC = healthCheck;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
logger.debug(error);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Utility to add error object in error buffer
|
|
66
|
-
* @param {*} error
|
|
67
|
-
*/
|
|
68
|
-
function addErrortoBuffer(error) {
|
|
69
|
-
try {
|
|
70
|
-
let errorObj = {
|
|
71
|
-
'error': error.stack
|
|
72
|
-
}
|
|
73
|
-
bufferedErrors.enq(errorObj);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
logger.debug(error);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* utility to format status template with dynamic values
|
|
81
|
-
*/
|
|
82
|
-
function getFormattedData() {
|
|
83
|
-
const Agent = require('./agent').Agent.getAgent();
|
|
84
|
-
const appInfo = Agent.applicationInfo;
|
|
85
|
-
const deployedApplications = appInfo && appInfo.serverInfo && appInfo.serverInfo.deployedApplications;
|
|
86
|
-
const appLoc = deployedApplications[0].deployedPath;
|
|
87
|
-
const formattedSnapshot = util.format(statusTemplate, new Date().toString(), appLoc, appInfo.serverInfo.name, commonUtils.getFramework(), commonUtils.getWSHealthStatus(), appInfo.policyVersion, getKeyValPairs(lastHC.stats), getKeyValPairs(lastHC.serviceStatus), JSON.stringify(getBufferedErrors()), JSON.stringify(getBufferedHC()));
|
|
88
|
-
return formattedSnapshot;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* utility to write snapshot in snapshot file
|
|
93
|
-
*/
|
|
94
|
-
function writeSnapshot() {
|
|
95
|
-
const snapshot = getFormattedData();
|
|
96
|
-
commonUtils.removeOlderSnapshots();
|
|
97
|
-
fs.writeFile(statusFile, snapshot, { mode: 0o660 }, function (err) {
|
|
98
|
-
if (err) {
|
|
99
|
-
logger.debug(err.message);
|
|
100
|
-
const LogMessage = require('./LogMessage');
|
|
101
|
-
const logMessage = new LogMessage.logMessage("DEBUG", 'Error in creating snapshot file', __filename, err);
|
|
102
|
-
commonUtils.addLogEventtoBuffer(logMessage);
|
|
103
|
-
} else {
|
|
104
|
-
logger.info('Snapshot updated to file: %s', statusFile);
|
|
105
|
-
fs.chmod(statusFile, 0o660, (err) => {
|
|
106
|
-
if (err) {
|
|
107
|
-
addErrortoBuffer(err);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* return buffered error instance.
|
|
115
|
-
*/
|
|
116
|
-
function getRingBufferedErrors() {
|
|
117
|
-
return bufferedErrors;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* return buffered HC list without null, undefined
|
|
122
|
-
*/
|
|
123
|
-
function getBufferedHC() {
|
|
124
|
-
try {
|
|
125
|
-
HC = lodash.compact(bufferedHC._elements);
|
|
126
|
-
} catch (error) {
|
|
127
|
-
logger.debug(error);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return HC;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* returns buffered error list without null, undefined
|
|
134
|
-
*/
|
|
135
|
-
function getBufferedErrors() {
|
|
136
|
-
try {
|
|
137
|
-
errors = lodash.compact(bufferedErrors._elements);
|
|
138
|
-
} catch (error) {
|
|
139
|
-
logger.debug(error);
|
|
140
|
-
}
|
|
141
|
-
return errors;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Utility to get key val pairs from json object
|
|
146
|
-
* @param {*} jsonObject
|
|
147
|
-
*/
|
|
148
|
-
function getKeyValPairs(jsonObject) {
|
|
149
|
-
let statStr = njsAgentConstants.EMPTY_STR;
|
|
150
|
-
for (const key in jsonObject) {
|
|
151
|
-
statStr = statStr + key + ': ' + jsonObject[key] + '\n';
|
|
152
|
-
}
|
|
153
|
-
return statStr;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
module.exports = {
|
|
157
|
-
statusTemplate,
|
|
158
|
-
addHCtoBuffer,
|
|
159
|
-
getFormattedData,
|
|
160
|
-
writeSnapshot,
|
|
161
|
-
addErrortoBuffer,
|
|
162
|
-
getRingBufferedErrors,
|
|
163
|
-
getBufferedHC,
|
|
164
|
-
getBufferedErrors
|
|
165
|
-
};
|