@newrelic/security-agent 2.0.0 → 2.1.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 +17 -0
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +36 -6
- package/lib/instrumentation-security/core/request-manager.js +37 -7
- package/lib/instrumentation-security/core/sec-utils.js +5 -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 +6 -27
- 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 +15 -9
- 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 +19 -0
- package/lib/nr-security-agent/lib/core/ExceptionReporting.js +1 -0
- package/lib/nr-security-agent/lib/core/agent.js +3 -0
- package/lib/nr-security-agent/lib/core/commonUtils.js +132 -4
- package/lib/nr-security-agent/lib/core/connections/websocket/response/IASTUtils.js +28 -2
- package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js +36 -4
- 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 +17 -2
- package/lib/nr-security-agent/lib/core/health-check.js +6 -1
- package/lib/nr-security-agent/lib/core/sec-util.js +5 -1
- package/lib/nr-security-agent/lib/core/websocket-client.js +31 -8
- package/lib/nr-security-api/index.js +47 -15
- package/package.json +3 -2
|
@@ -66,17 +66,17 @@ function initialize(shim, mod, moduleName) {
|
|
|
66
66
|
* @param {*} req
|
|
67
67
|
*/
|
|
68
68
|
function extractParams(shim, req) {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
69
|
+
const transaction = shim.tracer.getTransaction();
|
|
70
|
+
if (transaction) {
|
|
71
71
|
|
|
72
|
-
let request = requestManager.getRequestFromId(
|
|
72
|
+
let request = requestManager.getRequestFromId(transaction.id);
|
|
73
73
|
if (req.params && request) {
|
|
74
74
|
try {
|
|
75
75
|
Object.keys(req.params).forEach(function (key) {
|
|
76
76
|
if (req.params[key]) {
|
|
77
77
|
if (!request.parameterMap[key]) {
|
|
78
78
|
request.parameterMap[key] = new Array(req.params[key].toString());
|
|
79
|
-
requestManager.setRequest(
|
|
79
|
+
requestManager.setRequest(transaction.id, request);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
});
|
|
@@ -91,7 +91,7 @@ function extractParams(shim, req) {
|
|
|
91
91
|
Object.keys(query).forEach(function (key) {
|
|
92
92
|
if (query[key]) {
|
|
93
93
|
request.parameterMap[key] = new Array(query[key].toString());
|
|
94
|
-
requestManager.setRequest(
|
|
94
|
+
requestManager.setRequest(transaction.id, request);
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
} catch (error) {
|
|
@@ -100,7 +100,7 @@ function extractParams(shim, req) {
|
|
|
100
100
|
}
|
|
101
101
|
if (req.route && req.route.path && request) {
|
|
102
102
|
request.uri = req.route.path;
|
|
103
|
-
requestManager.setRequest(
|
|
103
|
+
requestManager.setRequest(transaction.id, request);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
}
|
|
106
|
+
}
|
|
@@ -24,6 +24,10 @@ const url = require('url');
|
|
|
24
24
|
* @param {*} moduleName
|
|
25
25
|
*/
|
|
26
26
|
function initialize(shim, mod, moduleName) {
|
|
27
|
+
if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.ssrf){
|
|
28
|
+
logger.warn('ssrf detection is disabled');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
27
31
|
logger.info('Instrumenting ' + moduleName);
|
|
28
32
|
fetchHook(shim, mod, "fetch");
|
|
29
33
|
let util = shim.require('./lib/core/util');
|
|
@@ -19,6 +19,10 @@ const logger = API.getLogger();
|
|
|
19
19
|
* @param {*} moduleName
|
|
20
20
|
*/
|
|
21
21
|
function initialize(shim, mod, moduleName){
|
|
22
|
+
if(API && API.getNRAgent() && API.getNRAgent().config.security.exclude_from_iast_scan.iast_detection_category.xpath_injection){
|
|
23
|
+
logger.warn('xpath_injection detection is disabled');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
22
26
|
logger.info('Instrumenting '+ moduleName)
|
|
23
27
|
|
|
24
28
|
if(moduleName==='xpath'){
|
|
@@ -17,7 +17,7 @@ const njsAgentConstants = require('./lib/core/sec-agent-constants');
|
|
|
17
17
|
const NRLogger = API.newrelic.shim.logger.child({ component: 'security-agent' });
|
|
18
18
|
NRLogger.info("Starting New Relic Node.js Security Agent ");
|
|
19
19
|
try {
|
|
20
|
-
if (NRAgent.config.security.enabled){
|
|
20
|
+
if (NRAgent.config.security.enabled) {
|
|
21
21
|
require('../instrumentation-security');
|
|
22
22
|
}
|
|
23
23
|
} catch (error) {
|
|
@@ -117,11 +117,44 @@ function initialize() {
|
|
|
117
117
|
if (NRAgent) {
|
|
118
118
|
NRAgent.on('started', () => {
|
|
119
119
|
NRLogger.info("Started New Relic Node.js Security Agent");
|
|
120
|
+
logger.info('NR Agent started with agent_run_id:', NRAgent.config.run_id, NRAgent.config.account_id);
|
|
121
|
+
if (Agent.getAgent().status.getStatus() != 'connecting') {
|
|
122
|
+
Agent.getAgent().delayed = false;
|
|
123
|
+
wsClient.obeyReconnect();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
120
126
|
if (NRAgent.config.security.enabled || !NRAgent.config.high_security) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
let delayFromConfig = parseInt(NRAgent.config.security.scan_schedule.delay);
|
|
128
|
+
let delay = delayFromConfig;
|
|
129
|
+
if (isNaN(delay) || delay < 0) {
|
|
130
|
+
delay = 0;
|
|
131
|
+
}
|
|
132
|
+
NRAgent.config.security.scan_schedule.delay = delay;
|
|
133
|
+
const allowSampling = NRAgent.config.security.scan_schedule.always_sample_traces;
|
|
134
|
+
|
|
135
|
+
if (delay >= 0) {
|
|
136
|
+
logger.debug("IAST delay is set to:", delay);
|
|
137
|
+
logger.info("Security Agent delay scan time is set to:", commonUtils.getScheduledScanTime(delay))
|
|
138
|
+
Agent.getAgent().delayed = true;
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
Agent.getAgent().delayed = false;
|
|
141
|
+
logger.info("IAST scanning delay of %s minutes over", delay);
|
|
142
|
+
commonUtils.honourDurationConfiguration();
|
|
143
|
+
commonUtils.executeCronSchedule();
|
|
144
|
+
if (!allowSampling) {
|
|
145
|
+
wsClient.initialize();
|
|
146
|
+
Agent.getAgent().status.setStatus('connecting');
|
|
147
|
+
Agent.setNRAgent(NRAgent);
|
|
148
|
+
}
|
|
149
|
+
}, delay * 60000);
|
|
150
|
+
}
|
|
151
|
+
logger.info("Security Agent is running with config:", JSON.stringify(NRAgent.config.security));
|
|
152
|
+
if (allowSampling) {
|
|
153
|
+
wsClient.initialize();
|
|
154
|
+
Agent.getAgent().status.setStatus('connecting');
|
|
155
|
+
Agent.setNRAgent(NRAgent);
|
|
156
|
+
}
|
|
157
|
+
|
|
125
158
|
}
|
|
126
159
|
else {
|
|
127
160
|
logger.warn("security.enabled flag is set to false");
|
|
@@ -11,6 +11,7 @@ const logger = logs.getLogger();
|
|
|
11
11
|
const initLogger = logs.getInitLogger();
|
|
12
12
|
const LinkingMetaData = require('./LinkingMetadata');
|
|
13
13
|
const LogMessage = require('./LogMessage');
|
|
14
|
+
const lodash = require('lodash');
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
/* eslint-disable camelcase */
|
|
@@ -37,6 +38,22 @@ function AuthHeaders() {
|
|
|
37
38
|
authHeaders["NR-CSEC-IAST-DATA-TRANSFER-MODE"] = "PULL";
|
|
38
39
|
authHeaders['NR-CSEC-ENTITY-NAME'] = metadata['entity.name'] ? metadata['entity.name'] : '';
|
|
39
40
|
authHeaders['NR-CSEC-ENTITY-GUID'] = metadata['entity.guid'] ? metadata['entity.guid'] : '';
|
|
41
|
+
let instanceCount = parseInt(NRAgent.config.security.scan_controllers.scan_instance_count);
|
|
42
|
+
if (isNaN(instanceCount)) {
|
|
43
|
+
instanceCount = 0;
|
|
44
|
+
}
|
|
45
|
+
if (instanceCount < 0 || instanceCount > 1) {
|
|
46
|
+
instanceCount = 0;
|
|
47
|
+
}
|
|
48
|
+
authHeaders['NR-CSEC-IAST-SCAN-INSTANCE-COUNT'] = instanceCount ? instanceCount : 0
|
|
49
|
+
if ((NRAgent.config.security.iast_test_identifier && !lodash.isEmpty(NRAgent.config.security.iast_test_identifier)) || process.env.NEW_RELIC_SECURITY_IAST_TEST_IDENTIFIER) {
|
|
50
|
+
let iast_test_identifier = NRAgent.config.security.iast_test_identifier || process.env.NEW_RELIC_SECURITY_IAST_TEST_IDENTIFIER
|
|
51
|
+
authHeaders['NR-CSEC-IAST-TEST-IDENTIFIER'] = iast_test_identifier;
|
|
52
|
+
logger.debug("iast_test_identifier is:", iast_test_identifier);
|
|
53
|
+
authHeaders['NR-CSEC-IAST-SCAN-INSTANCE-COUNT'] = 1;
|
|
54
|
+
}
|
|
55
|
+
authHeaders['NR-CSEC-PROCESS-START-TIME'] = Date.now() - Math.floor(process.uptime() * 1000);
|
|
56
|
+
authHeaders['NR-CSEC-IGNORED-VUL-CATEGORIES'] = commonUtils.listSkipDetectionCategory();
|
|
40
57
|
} catch (error) {
|
|
41
58
|
logger.error("Exception in preparing auth headers:", error);
|
|
42
59
|
const logMessage = new LogMessage.logMessage("SEVERE", 'Exception in preparing auth headers', __filename, error);
|
|
@@ -76,6 +93,8 @@ function getInstance() {
|
|
|
76
93
|
return authHeaders;
|
|
77
94
|
}
|
|
78
95
|
|
|
96
|
+
|
|
97
|
+
|
|
79
98
|
module.exports = {
|
|
80
99
|
getInstance
|
|
81
100
|
};
|
|
@@ -89,6 +89,7 @@ function exceptionReporting(exception, request, responseCode) {
|
|
|
89
89
|
this.timestamp = Date.now();
|
|
90
90
|
this.httpRequest = request;
|
|
91
91
|
this.httpRequest.route = request.uri ? request.uri : request.url;
|
|
92
|
+
this.httpRequest.requestURI = request.url.split('?')[0];
|
|
92
93
|
this.exception = null;
|
|
93
94
|
this.counter = 1;
|
|
94
95
|
this.responseCode = responseCode ? responseCode : null
|
|
@@ -42,6 +42,7 @@ function Agent(applicationInfo, logger, client, status) {
|
|
|
42
42
|
this.logger = logger;
|
|
43
43
|
this.status = status;
|
|
44
44
|
this.wsStatus = 0;
|
|
45
|
+
this.delayed = false;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
Agent.prototype = {};
|
|
@@ -96,6 +97,7 @@ Agent.prototype.setAgentStatus = function setAgentStatus(status) {
|
|
|
96
97
|
Agent.eventEmitter.emit(EVENTS.AGENT_STATUS_UPDATED_EVENT, status);
|
|
97
98
|
};
|
|
98
99
|
|
|
100
|
+
|
|
99
101
|
Agent.EVENTS = EVENTS;
|
|
100
102
|
|
|
101
103
|
Agent.eventEmitter = new EventEmitter();
|
|
@@ -138,6 +140,7 @@ Agent.setNRAgent = function setNRAgent(agent) {
|
|
|
138
140
|
}
|
|
139
141
|
};
|
|
140
142
|
|
|
143
|
+
|
|
141
144
|
module.exports = {
|
|
142
145
|
Agent
|
|
143
146
|
};
|
|
@@ -23,6 +23,23 @@ let websocketHealth;
|
|
|
23
23
|
|
|
24
24
|
const ringBuffer = require('ringbufferjs');
|
|
25
25
|
const bufferedLogEvents = new ringBuffer(10);
|
|
26
|
+
const { CronJob } = require('cron');
|
|
27
|
+
let scanStartTime = 0;
|
|
28
|
+
let trafficStartedTime = 0;
|
|
29
|
+
const cron = require('cron');
|
|
30
|
+
|
|
31
|
+
const skip_detection_category_map = {
|
|
32
|
+
insecure_settings: ['CRYPTO', 'HASH', 'RANDOM', 'SECURE_COOKIE', 'TRUSTBOUNDARY'],
|
|
33
|
+
invalid_file_access: ['FILE_OPERATION', 'FILE_INTEGRITY'],
|
|
34
|
+
sql_injection: ['SQL_DB_COMMAND'],
|
|
35
|
+
nosql_injection: ['NOSQL_DB_COMMAND'],
|
|
36
|
+
ldap_injection: ['LDAP'],
|
|
37
|
+
javascript_injection: ['JS_INJECTION'],
|
|
38
|
+
command_injection: ['SYSTEM_COMMAND'],
|
|
39
|
+
xpath_injection: ['XPATH'],
|
|
40
|
+
ssrf: ['HTTP_REQUEST'],
|
|
41
|
+
rxss: ['REFLECTED_XSS']
|
|
42
|
+
}
|
|
26
43
|
|
|
27
44
|
|
|
28
45
|
/**
|
|
@@ -271,7 +288,7 @@ function removeOlderlogfiles() {
|
|
|
271
288
|
return new Date(secondStat.mtime).getTime() - new Date(firstStat.mtime).getTime();
|
|
272
289
|
});
|
|
273
290
|
let maxLogfile = process.env.NR_CSEC_DEBUG_LOGFILE_MAX_COUNT;
|
|
274
|
-
if(isNaN(maxLogfile)){
|
|
291
|
+
if (isNaN(maxLogfile)) {
|
|
275
292
|
maxLogfile = 2;
|
|
276
293
|
}
|
|
277
294
|
for (let i = maxLogfile; i < sorted.length; i++) {
|
|
@@ -409,7 +426,7 @@ function addLogEventtoBuffer(logMessage) {
|
|
|
409
426
|
* Utility to get framework from APM
|
|
410
427
|
* @returns
|
|
411
428
|
*/
|
|
412
|
-
function getFramework(){
|
|
429
|
+
function getFramework() {
|
|
413
430
|
let framework = EMPTY_STR;
|
|
414
431
|
try {
|
|
415
432
|
framework = NRAgent.environment.get('Framework')[0];
|
|
@@ -419,14 +436,119 @@ function getFramework(){
|
|
|
419
436
|
return framework;
|
|
420
437
|
}
|
|
421
438
|
|
|
439
|
+
function listSkipDetectionCategory() {
|
|
440
|
+
let skipList = [];
|
|
441
|
+
try {
|
|
442
|
+
let skipCataegories = NRAgent.config.security.exclude_from_iast_scan.iast_detection_category;
|
|
443
|
+
for (const key in skipCataegories) {
|
|
444
|
+
if (skipCataegories[key]) {
|
|
445
|
+
skipList = skipList.concat(skip_detection_category_map[key]);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
logger.error("Error while processing skip detection categories:", error);
|
|
450
|
+
}
|
|
451
|
+
return skipList;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function refreshAgent() {
|
|
455
|
+
const { Agent } = require('./agent');
|
|
456
|
+
if (Agent.getAgent().status.getStatus() == 'disabled' || Agent.getAgent().delayed) {
|
|
457
|
+
Agent.getAgent().delayed = false;
|
|
458
|
+
const wsClient = Agent.getAgent().client;
|
|
459
|
+
Agent.getAgent().status.setStatus('connecting');
|
|
460
|
+
wsClient.obeyReconnect();
|
|
461
|
+
honourDurationConfiguration();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function executeCronSchedule() {
|
|
466
|
+
try {
|
|
467
|
+
let schedule = NRAgent.config.security.scan_schedule.schedule;
|
|
468
|
+
if (lodash.isEmpty(schedule)) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
logger.debug("Schedule config is set to:", schedule);
|
|
472
|
+
schedule = schedule.replace(/\?/g, '*');
|
|
473
|
+
logger.info("Security Agent scheduled time is set to:", getScheduledScanTime(Math.ceil(cron.timeout(schedule) / 60000)))
|
|
474
|
+
const job = new cron.CronJob(
|
|
475
|
+
schedule, // cronTime
|
|
476
|
+
function () {
|
|
477
|
+
logger.debug('Cron schedule invoked');
|
|
478
|
+
logger.info("Security Agent scheduled time is set to:", getScheduledScanTime(Math.ceil(cron.timeout(schedule) / 60000)))
|
|
479
|
+
refreshAgent()
|
|
480
|
+
}, // onTick
|
|
481
|
+
null, // onComplete
|
|
482
|
+
true, // start
|
|
483
|
+
);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
logger.error("Error while processing schedule. Please check schedule cron expression", error);
|
|
486
|
+
shutDownAgent();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function honourDurationConfiguration() {
|
|
492
|
+
let durationFromConfig = parseInt(NRAgent.config.security.scan_schedule.duration);
|
|
493
|
+
let duration = durationFromConfig;
|
|
494
|
+
if (isNaN(duration) || duration < 0) {
|
|
495
|
+
duration = 0;
|
|
496
|
+
}
|
|
497
|
+
logger.debug("IAST duration is set to:", duration);
|
|
498
|
+
NRAgent.config.security.scan_schedule.duration = duration;
|
|
499
|
+
if (duration < 1) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
logger.info("Security Agent shutdown is set to:", getScheduledScanTime(duration))
|
|
503
|
+
setTimeout(() => {
|
|
504
|
+
shutDownAgent();
|
|
505
|
+
}, duration * 60000);
|
|
506
|
+
}
|
|
422
507
|
/**
|
|
423
508
|
* Utility to send error to error inbox of APM
|
|
424
509
|
* @param {*} error
|
|
425
510
|
*/
|
|
426
|
-
function sendErrorToErrorInbox(error){
|
|
511
|
+
function sendErrorToErrorInbox(error) {
|
|
427
512
|
API.newrelic.noticeError(error);
|
|
428
513
|
}
|
|
429
514
|
|
|
515
|
+
const getScheduledScanTime = (delayInMinutes) => {
|
|
516
|
+
// Get the current time
|
|
517
|
+
const currentTime = new Date();
|
|
518
|
+
|
|
519
|
+
// Add the given minutes to the current time
|
|
520
|
+
currentTime.setMinutes(currentTime.getMinutes() + delayInMinutes);
|
|
521
|
+
|
|
522
|
+
const year = currentTime.getFullYear();
|
|
523
|
+
const month = currentTime.toLocaleString("default", { month: "long" });
|
|
524
|
+
const day = currentTime.toLocaleString("default", { weekday: "long" });
|
|
525
|
+
const date = currentTime.getDate();
|
|
526
|
+
const hours = currentTime.getHours();
|
|
527
|
+
const minutes = currentTime.getMinutes();
|
|
528
|
+
const seconds = currentTime.getSeconds();
|
|
529
|
+
const futureUpdatedTime = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
|
530
|
+
const futureDateTime = `${day}, ${month} ${date} ${year} ${futureUpdatedTime}`;
|
|
531
|
+
return futureDateTime
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
function shutDownAgent() {
|
|
535
|
+
const { Agent } = require('./agent');
|
|
536
|
+
if (NRAgent.config.security.scan_schedule.always_sample_traces) {
|
|
537
|
+
Agent.getAgent().delayed = true;
|
|
538
|
+
logger.warn('Scan duration completed, IAST going under hibernate mode')
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (Agent.getAgent().status.getStatus() === 'disabled') {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
Agent.getAgent().status.setStatus('disabled');
|
|
545
|
+
const wsClient = Agent.getAgent().client;
|
|
546
|
+
wsClient.closeWS();
|
|
547
|
+
logger.warn("Security Agent status is disabled");
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
430
552
|
|
|
431
553
|
module.exports = {
|
|
432
554
|
getUUID,
|
|
@@ -452,5 +574,11 @@ module.exports = {
|
|
|
452
574
|
addLogEventtoBuffer,
|
|
453
575
|
getLogEvents,
|
|
454
576
|
getFramework,
|
|
455
|
-
|
|
577
|
+
listSkipDetectionCategory,
|
|
578
|
+
executeCronSchedule,
|
|
579
|
+
honourDurationConfiguration,
|
|
580
|
+
scanStartTime,
|
|
581
|
+
trafficStartedTime,
|
|
582
|
+
sendErrorToErrorInbox,
|
|
583
|
+
getScheduledScanTime,
|
|
456
584
|
};
|
|
@@ -6,8 +6,13 @@ const { Agent } = require('../../../agent');
|
|
|
6
6
|
let pendingRequestIds = new Set();
|
|
7
7
|
let completedRequestsMap = new Map();
|
|
8
8
|
const PolicyManager = require('../../../Policy');
|
|
9
|
+
const API = require('../../../../../../nr-security-api');
|
|
9
10
|
let batchSize;
|
|
10
|
-
const batchthreshold =
|
|
11
|
+
const batchthreshold = 200;
|
|
12
|
+
const logs = require('../../../logging');
|
|
13
|
+
let logger = logs.getLogger();
|
|
14
|
+
const commonUtils = require('../../../commonUtils');
|
|
15
|
+
|
|
11
16
|
function getPendingRequestIds() {
|
|
12
17
|
return [...pendingRequestIds];
|
|
13
18
|
}
|
|
@@ -56,6 +61,7 @@ function IASTCleanup() {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
function generateIASTDataRequest() {
|
|
64
|
+
|
|
59
65
|
const policyInstance = PolicyManager.getInstance();
|
|
60
66
|
if (policyInstance.data && policyInstance.data.vulnerabilityScan && policyInstance.data.vulnerabilityScan.iastScan) {
|
|
61
67
|
batchSize = batchSize ? batchSize : policyInstance.data.vulnerabilityScan.iastScan.probing.batchSize;
|
|
@@ -64,13 +70,33 @@ function generateIASTDataRequest() {
|
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
if (getCompletedRequestsMap().size >= batchSize || batchSize < batchthreshold || getPendingRequestIds().length < batchSize) {
|
|
67
|
-
batchSize = (2 * batchSize);
|
|
73
|
+
batchSize = (2 * batchSize) - getPendingRequestIds().length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const NRAgent = API.getNRAgent();
|
|
77
|
+
let scanRateLimit = 3600;
|
|
78
|
+
let scanRateLimitFromConfig = parseInt(NRAgent.config.security.scan_controllers.iast_scan_request_rate_limit);
|
|
79
|
+
scanRateLimit = scanRateLimitFromConfig
|
|
80
|
+
if (isNaN(scanRateLimitFromConfig)) {
|
|
81
|
+
scanRateLimit = 3600;
|
|
68
82
|
}
|
|
83
|
+
if (scanRateLimit >= 0 || scanRateLimit < 0) {
|
|
84
|
+
batchSize = parseInt(scanRateLimit / 12);
|
|
85
|
+
if (scanRateLimit < 12) {
|
|
86
|
+
batchSize = 1;
|
|
87
|
+
} else if (scanRateLimit > 3600) {
|
|
88
|
+
batchSize = 300;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
69
92
|
let object = {};
|
|
70
93
|
object['jsonName'] = 'iast-data-request';
|
|
71
94
|
object['applicationUUID'] = Agent.getAgent().applicationInfo.applicationUUID;
|
|
72
95
|
object['appEntityGuid'] = Agent.getAgent().applicationInfo.appEntityGuid;
|
|
73
96
|
object['appAccountId'] = Agent.getAgent().applicationInfo.appAccountId;
|
|
97
|
+
if (batchSize && batchSize > 100) {
|
|
98
|
+
batchSize = 100;
|
|
99
|
+
}
|
|
74
100
|
object["batchSize"] = batchSize ? batchSize : 300;
|
|
75
101
|
object['pendingRequestIds'] = getPendingRequestIds();
|
|
76
102
|
object['completedRequests'] = Object.fromEntries(getCompletedRequestsMap());
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/fuzz-request-handler.js
CHANGED
|
@@ -24,6 +24,9 @@ const PolicyManager = require('../../../Policy');
|
|
|
24
24
|
|
|
25
25
|
const find = `${SLASH}{{NR_CSEC_VALIDATOR_HOME_TMP}}`;
|
|
26
26
|
const CSEC_HOME_TMP_CONST = new RegExp(find, 'g');
|
|
27
|
+
const commonUtils = require('../../../commonUtils');
|
|
28
|
+
|
|
29
|
+
const CSEC_HOME_TMP_CONST_ENCODED = new RegExp('%7B%7BNR_CSEC_VALIDATOR_HOME_TMP%7D%7D', 'g');
|
|
27
30
|
|
|
28
31
|
let logger;
|
|
29
32
|
let lastFuzzEventTime = 0;
|
|
@@ -31,13 +34,26 @@ let iastIntervalConst;
|
|
|
31
34
|
let coolDownIntervalConst;
|
|
32
35
|
let additionalCoolDownTime = 0;
|
|
33
36
|
let fuzzedApiIDSet = new Set();
|
|
37
|
+
const API = require('../../../../../../nr-security-api');
|
|
38
|
+
const NRAgent = API.getNRAgent();
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
42
|
* utility to start IAST Schedular
|
|
38
43
|
*/
|
|
39
44
|
function startIASTSchedular() {
|
|
40
|
-
Agent.getAgent().
|
|
45
|
+
if (Agent.getAgent().delayed) {
|
|
46
|
+
let delayFromConfig = parseInt(NRAgent.config.security.scan_schedule.delay);
|
|
47
|
+
let delay = delayFromConfig;
|
|
48
|
+
if (isNaN(delay) || delay < 0) {
|
|
49
|
+
delay = 0;
|
|
50
|
+
}
|
|
51
|
+
// logger.debug("IAST data pull request is scheduled at %s", require('../../../commonUtils').getScheduledScanTime(delay))
|
|
52
|
+
}
|
|
53
|
+
if (!Agent.getAgent().delayed) {
|
|
54
|
+
Agent.getAgent().client.dispatcher(IASTUtil.generateIASTDataRequest());
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
if (iastIntervalConst) {
|
|
42
58
|
clearInterval(iastIntervalConst);
|
|
43
59
|
}
|
|
@@ -50,8 +66,11 @@ function startIASTSchedular() {
|
|
|
50
66
|
if (isNaN(probingInterval)) {
|
|
51
67
|
probingInterval = 5;
|
|
52
68
|
}
|
|
53
|
-
|
|
69
|
+
|
|
54
70
|
iastIntervalConst = setInterval(() => {
|
|
71
|
+
if (Agent.getAgent().status.getStatus() == 'disabled' || Agent.getAgent().delayed) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
55
74
|
let data = IASTUtil.generateIASTDataRequest();
|
|
56
75
|
let currentTime = Date.now();
|
|
57
76
|
let completedListSize = IASTUtil.getCompletedRequestsMap().size;
|
|
@@ -86,9 +105,19 @@ function logScannedApiId(fuzzHeader, requestURL) {
|
|
|
86
105
|
|
|
87
106
|
// handler function to parse fuzz event and prepare fuzz request
|
|
88
107
|
function handler(json) {
|
|
108
|
+
if (Agent.getAgent().status.getStatus() == 'disabled') {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
89
111
|
setLastFuzzEventTime();
|
|
90
112
|
let rawFuzzRequest = json.arguments[0];
|
|
91
|
-
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
rawFuzzRequest = rawFuzzRequest.replace(CSEC_HOME_TMP_CONST, CSEC_HOME_TMP);
|
|
116
|
+
rawFuzzRequest = rawFuzzRequest.replace(CSEC_HOME_TMP_CONST_ENCODED, encodeURI(CSEC_HOME_TMP));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.debug("Error while replacing place holder", error);
|
|
119
|
+
}
|
|
120
|
+
|
|
92
121
|
let fuzzRequest;
|
|
93
122
|
try {
|
|
94
123
|
fuzzRequest = JSON.parse(rawFuzzRequest);
|
|
@@ -96,7 +125,7 @@ function handler(json) {
|
|
|
96
125
|
IASTUtil.addPendingRequestId(json.id);
|
|
97
126
|
} catch (error) {
|
|
98
127
|
logger.error('Parsing exeception in fuzz request: ', error);
|
|
99
|
-
|
|
128
|
+
|
|
100
129
|
const logMessage = new LogMessage.logMessage(SEVERE, 'Parsing exeception in fuzz request', __filename, error);
|
|
101
130
|
require('../../../commonUtils').addLogEventtoBuffer(logMessage);
|
|
102
131
|
}
|
|
@@ -177,6 +206,9 @@ function handleFuzzResponse(response, fuzzDetails) {
|
|
|
177
206
|
const { rawFuzzRequest, fuzzRequest } = fuzzDetails;
|
|
178
207
|
if (response) {
|
|
179
208
|
IASTUtil.removePendingRequestId(fuzzRequest.id);
|
|
209
|
+
if (commonUtils.scanStartTime == 0) {
|
|
210
|
+
commonUtils.scanStartTime = Date.now();
|
|
211
|
+
}
|
|
180
212
|
response.then(() => {
|
|
181
213
|
logger.info('Fuzz success: ' + rawFuzzRequest);
|
|
182
214
|
}).catch(() => {
|
package/lib/nr-security-agent/lib/core/connections/websocket/response/startup-handshake-handler.js
CHANGED
|
@@ -10,6 +10,10 @@ const initLogger = logs.getInitLogger();
|
|
|
10
10
|
const LOG_MESSAGES = require('../../../sec-agent-constants').LOG_MESSAGES;
|
|
11
11
|
const applicationInfoModule = require('../../../applicationinfo');
|
|
12
12
|
const LogMessage = require('../../../LogMessage');
|
|
13
|
+
const API = require('../../../../../../nr-security-api');
|
|
14
|
+
const NRAgent = API.getNRAgent();
|
|
15
|
+
const { Agent } = require('../../../agent');
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
module.exports.handler = function handler(json) {
|
|
15
19
|
try {
|
|
@@ -20,7 +24,7 @@ module.exports.handler = function handler(json) {
|
|
|
20
24
|
initLogger.info('Security Agent is now ACTIVE for', applicationInfoModule.getInstance().applicationUUID);
|
|
21
25
|
const logMessage = new LogMessage.logMessage("INFO", `Security Agent is ACTIVE for ${ applicationInfoModule.getInstance().applicationUUID}`, __filename, null);
|
|
22
26
|
require('../../../commonUtils').addLogEventtoBuffer(logMessage);
|
|
23
|
-
|
|
27
|
+
|
|
24
28
|
} catch (error) {
|
|
25
29
|
logger.error(LOG_MESSAGES.PARSING_EXP_WELCOME_MSG, error);
|
|
26
30
|
}
|
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
let logger;
|
|
7
|
+
const { Agent } = require('../../../agent');
|
|
7
8
|
module.exports.handler = function handler (json) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
if (Agent.getAgent().status.getStatus() != 'disabled'){
|
|
10
|
+
logger.info('WS reconnect received', json);
|
|
11
|
+
const wsClient = Agent.getAgent().client;
|
|
12
|
+
wsClient.obeyReconnect();
|
|
13
|
+
}
|
|
14
|
+
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
module.exports.setLogger = l => { logger = l };
|
|
@@ -176,6 +176,9 @@ SecWebSocket.prototype.reconnect = function reconnect() {
|
|
|
176
176
|
};
|
|
177
177
|
|
|
178
178
|
SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
|
|
179
|
+
if (Agent.getAgent().status.getStatus() == 'disabled') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
179
182
|
const { instance } = this;
|
|
180
183
|
if (initTimeOut) {
|
|
181
184
|
clearTimeout(initTimeOut);
|
|
@@ -194,13 +197,25 @@ SecWebSocket.prototype.obeyReconnect = function obeyReconnect() {
|
|
|
194
197
|
|
|
195
198
|
}
|
|
196
199
|
|
|
200
|
+
SecWebSocket.prototype.closeWS = function closeWS() {
|
|
201
|
+
const { instance } = this;
|
|
202
|
+
try {
|
|
203
|
+
instance.close();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
logger.error("Error while closing websocket", error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
197
209
|
SecWebSocket.prototype.pingWS = async function pingWS() {
|
|
210
|
+
if (Agent.getAgent().status.getStatus() != 'active') {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
198
213
|
const self = this;
|
|
199
214
|
let currentTime = Date.now();
|
|
200
215
|
|
|
201
216
|
let timeDiffInPing = (currentTime - lastPongtime) / 1000;
|
|
202
|
-
logger.debug("Websocket ready state is:",self.instance.readyState)
|
|
203
|
-
|
|
217
|
+
logger.debug("Websocket ready state is:", self.instance.readyState)
|
|
218
|
+
|
|
204
219
|
if (timeDiffInPing > 40 && lastPongtime != 0 && self.instance.readyState > 1) {
|
|
205
220
|
self.obeyReconnect();
|
|
206
221
|
lastPongtime = 0;
|
|
@@ -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
|
+
let 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
|
*
|
|
@@ -63,6 +65,9 @@ function HC(info) {
|
|
|
63
65
|
this.iastEventStats = EventStats.getInstance();
|
|
64
66
|
this.raspEventStats = EventStats.getInstance();
|
|
65
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);
|
|
66
71
|
|
|
67
72
|
|
|
68
73
|
/**
|
|
@@ -134,6 +134,7 @@ function generateSecEvent(securityMetadata) {
|
|
|
134
134
|
const isAPIBlocked = false;
|
|
135
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);
|
|
136
136
|
event.metaData.isClientDetectedFromXFF = isClientDetectedFromXFF;
|
|
137
|
+
event.metaData.skipScanParameters = NRAgent.config.security.exclude_from_iast_scan.http_request_parameters;
|
|
137
138
|
event.httpRequest.route = uri;
|
|
138
139
|
return event;
|
|
139
140
|
} catch (e) {
|
|
@@ -191,9 +192,12 @@ function generateExitEvent(secEvent) {
|
|
|
191
192
|
|
|
192
193
|
function sendEvent(secEvent) {
|
|
193
194
|
const agentModule = Agent.getAgent();
|
|
194
|
-
if (agentModule.status.getStatus() != 'connecting' && NRAgent.config.security.enabled) {
|
|
195
|
+
if (agentModule.status.getStatus() != 'connecting' && NRAgent.config.security.enabled && agentModule.status.getStatus() == 'active') {
|
|
195
196
|
agentModule.client.dispatcher(secEvent);
|
|
196
197
|
if (!firstEventSent && agentModule.status.getStatus() === 'active') {
|
|
198
|
+
if(commonUtils.trafficStartedTime == 0){
|
|
199
|
+
commonUtils.trafficStartedTime = Date.now();
|
|
200
|
+
}
|
|
197
201
|
initLogger.info("[STEP-8] => First event sent for validation. Security Agent started successfully", JSON.stringify(secEvent));
|
|
198
202
|
firstEventSent = true;
|
|
199
203
|
}
|